【问题标题】:Run task asynchronously in C#在 C# 中异步运行任务
【发布时间】:2012-09-27 15:06:29
【问题描述】:

我有一些在我的 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。我认为运行进程并在完成后自动关闭的异步线程可能很有用。但是它是如何工作的呢??

【问题讨论】:

  • 对于 excel 创建查找 BackgroundWorker。对于第二个循环,所有代码都是 UI 代码,因此无法线程化,但看起来不像是长时间运行的代码。你有多少行?您也可以暂停网格绘制,它可能会有所帮助。
  • 你确定这不是重复的吗? SO充满了类似的问题......
  • @AmiramKorach 下周将尝试使用 BackgroundWorker 进行方法调用,看看它是否可以帮助我。第二个循环大约有 512+ 行,出于视觉原因,整个想法是在每行中创建绿色背景。根本不可能创建在 UI 中运行的异步,因此 UI 在加载某些内容时不会冻结?和/或制作一个显示 UI 正在加载的加载微调器???
  • 只有在大部分过程没有更新 UI 时,您才能异步执行操作。如果大部分过程是 UI 更新,您将不会有任何收获,因为 UI 更新必须在 UI 线程中完成。对于批量 UI 更新,您可以暂停网格绘制,以便更新更快。

标签: c# winforms multithreading


【解决方案1】:

使用Task 怎么样(如果针对.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 i.e.: Fail.Exception.InnerException);
        }, TaskContinuationOptions.OnlyOnFaulted);

    }

【讨论】:

    【解决方案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 线程上。

    【讨论】:

      【解决方案3】:

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

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

      原因已在in this question 中介绍。但我更惊讶的是,这速度慢到你想让它异步。可能有一些简单的方法可以使此代码更具响应性 - 想到 Control.SuspendLayout().ResumeLayout()

      【讨论】:

        猜你喜欢
        • 2011-04-15
        • 1970-01-01
        • 2010-10-25
        • 2018-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多