在C#中异步运行任务

时间:2012-09-27 15:06:30

标签: c# winforms multithreading

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

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

EG。我通过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。运行该进程并在完成时自动关闭的异步线程可能对我有用。但它是如何运作的?

3 个答案:

答案 0 :(得分:3)

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

E.g。

    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 i.e.: Fail.Exception.InnerException);
        }, TaskContinuationOptions.OnlyOnFaulted);

    }

答案 1 :(得分:2)

我回答了一个非常相似的问题here

归结为使用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线程上。

答案 2 :(得分:0)

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

对于你的第二个例子,你会发现你不能把它放在后台线程上,因为它似乎是操纵UI的代码。具体来说,如果您BackgroundWorker的{​​{1}}事件处理程序的实现,或传递给DoWork的委托,或普通旧线程的方法触及UI,您很可能(/某些?)获得一个异常,说明“跨线程操作无效”。

理由是in this question。但是我更惊讶的是,实际上这很慢,你想让它异步。可能有一些简单的方法可以使这些代码更具响应性 - Task.Factory.StartNew()Control.SuspendLayout()会让人想起。