在C#中异步运行任务

我有一些在我的WinForms应用程序中运行的过程繁重的任务。 问题是,在运行时,它会冻结UI(UI主线程)。

我还没有在C#中使用线程和委托做那么多,这就是为什么我希望有人可以帮助我,如何处理那些过程繁重的任务,而不会冻结UI,因此用户不认为应用程序崩溃在等待?

例如。 我通过FrontController打电话需要时间:

 _controller.testExportExcel(wrapper, saveDialog.FileName); 

因为它正在创建一个Excel文件。 在工作时,我不会让应用程序在UI上做出响应。

流程繁重的任务的另一个例子可能是这样的:

 private void dataGridView_liste_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) { if (e.ListChangedType != ListChangedType.ItemDeleted) { foreach (DataGridViewRow r in dataGridView_liste.Rows) { DataGridViewCellStyle red = dataGridView_liste.DefaultCellStyle.Clone(); red.BackColor = Color.LightGreen; if (r.Cells["News"].Value != null && (bool)r.Cells["News"].Value == true) r.DefaultCellStyle = red; } } } 

foreach循环需要时间,并冻结UI。 运行该进程并在完成时自动关闭的异步线程可能对我有用。 但它是如何工作的?

如何使用任务 (如果目标.net 4)? 这被视为BackgroundWorker类的替代,因为它支持嵌套(父/子任务),任务延续等。

例如

  private void dataGridView_liste_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) { Task t = Task.Factory.StartNew(() => { // do your processing here - remember to call Invoke or BeginInvoke if // calling a UI object. }); t.ContinueWith((Success) => { // callback when task is complete. }, TaskContinuationOptions.NotOnFaulted); t.ContinueWith((Fail) => { //log the exception ie: Fail.Exception.InnerException); }, TaskContinuationOptions.OnlyOnFaulted); } 

我在这里回答了一个非常相似的问题

归结为使用BackgroundWorker。

msdn提供了一个例子:

 using System.ComponentModel; using System.Windows; using System.Windows.Controls; namespace SL_BackgroundWorker_CS { public partial class Page : UserControl { private BackgroundWorker bw = new BackgroundWorker(); public Page() { InitializeComponent(); bw.WorkerReportsProgress = true; bw.WorkerSupportsCancellation = true; bw.DoWork += new DoWorkEventHandler(bw_DoWork); bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); } private void buttonStart_Click(object sender, RoutedEventArgs e) { if (bw.IsBusy != true) { bw.RunWorkerAsync(); } } private void buttonCancel_Click(object sender, RoutedEventArgs e) { if (bw.WorkerSupportsCancellation == true) { bw.CancelAsync(); } } private void bw_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; for (int i = 1; (i <= 10); i++) { if ((worker.CancellationPending == true)) { e.Cancel = true; break; } else { // Perform a time consuming operation and report progress. System.Threading.Thread.Sleep(500); worker.ReportProgress((i * 10)); } } } private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if ((e.Cancelled == true)) { this.tbProgress.Text = "Canceled!"; } else if (!(e.Error == null)) { this.tbProgress.Text = ("Error: " + e.Error.Message); } else { this.tbProgress.Text = "Done!"; } } private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%"); } } } 

在DoWork事件处理程序中运行的所有内容都是异步的。
在ProgessChanged / RunWorkCompleted的事件处理程序中运行的所有内容都在UI线程上。

对于您的第一个示例,调用_controller.testExportExcel()BackgroundWorkerTask Parallel Library调用(即Task.Factory.StartNew(...) )将适合满足您保持UI响应的要求。 大量的例子浮出水面,包括其他答案。

对于你的第二个例子,你会发现你不能把它放在后台线程上,因为它似乎是操纵UI的代码。 具体来说,如果您的BackgroundWorkerDoWork事件处理程序的实现,或传递给Task.Factory.StartNew()的委托,或普通旧线程的方法触摸UI,您很可能(/确定?)得到一个exception,说明“跨线程操作无效”。

这个问题涵盖了这个原因。 但我更惊讶的是,实际上这很慢,你想让它异步。 可能有一些简单的方法可以使这些代码更具响应性 – .ResumeLayout() Control.SuspendLayout().ResumeLayout()