【问题标题】:Run code without block main thread在不阻塞主线程的情况下运行代码
【发布时间】:2015-08-22 20:51:28
【问题描述】:

我需要生成 n 个随机字符串,这个过程可能需要一段时间并阻塞主线程 UI。为了避免这种情况并让用户在进程运行时使用程序,我决定使用backGroundWorker。但它没有很好地工作,主线程仍然被阻塞。在我的DoWork 活动中,我有这样的事情:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
           // using Invoke because I change value of some controls, e.g,   ListView, labels and a progressbar
            this.Invoke((Action)delegate
            {
                for (int i = 0; i < nSteps; ++i)
                {
                    string s = getnStr();
                    // ...
                    int progress = (int)(100.0 / nSteps * (i + 1));
                    backgroundWorker1.ReportProgress(progress);
                }
            });
        }

即使我在循环内调用ReportProgress(),进度条也只会在循环完成时改变它的值。

为什么会发生这种情况,我该如何解决?

【问题讨论】:

  • 如果在 Progress 事件处理程序中发生更新,则不需要 Invoke。
  • 我也在循环内更换标签。
  • 您也可以使用 ReportProgress 更改标签,提示:它有 UserData

标签: c# .net multithreading backgroundworker


【解决方案1】:

调用...这就是你搞错的地方。您只是创建了一个后台工作程序,以便将代码调用回主线程(这就是 Invoke 所做的)。

您实际上应该仅将调用用于控件访问,或者仅在 ReportProgress 处理程序中修改控件,然后确保每次需要时都调用 ReportProgress 方法。

编辑:澄清:您的调用问题是您正在为后台工作人员应该执行的整个工作负载调用 UI 线程。

【讨论】:

    【解决方案2】:

    另一个答案解释了您所看到的行为。请先阅读。以下是解决此问题的方法:

    BackgroundWorker 已过时,因为我们现在有 Taskawait。它自动化了很多。您的代码可能应该如下所示:

                //In the button click handler:
                for (int i = 0; i < nSteps; ++i)
                {
                    await DoSomeWorkAsync();
                    int progress = (int)(100.0 / nSteps * (i + 1));
                    SetProgressBarValue(progress);
                }
    

    真的就是这样。您需要确保DoSomeWorkAsync 不会阻塞。它必须返回一个Task

    【讨论】:

      【解决方案3】:

      除了报告进度之外,ReportProgress() 方法还可以用作通用异步事件调度器(例如更新 UI 控件中的文本):

       for (int i = 0; i < nSteps; ++i)
       {
         string s = getnStr();
      
         // Update text
         backgroundWorker1.ReportProgress(0, "My text..");    
      
         // Update progress
         int progress = (int)(100.0 / nSteps * (i + 1));
         backgroundWorker1.ReportProgress(progress);
       }
      

      ProgressChanged 事件处理程序看起来像这样:

      void backgroundWorker1_ProgressChanged(object sender,  ProgressChangedEventArgs e)
      {
           if (e.UserState != null)
           {
                 // Handle text update
                 label1.Text = (string)e.UserState;
                 return;
           } 
      
           progressBar1.Value = e.ProgressPercentage;   
      }
      

      【讨论】:

      • Thinks 是我在 ProgressChanged 事件处理程序 =) 中所做的,因此我可以在 listView 上添加项目、更改 labelText 或 progressBar。我使用is 运算符来确定要做什么
      • ProgressChanged 调用RTB.AppendText() 只是以同样的方式阻塞线程。有什么建议可以解决这个问题吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-13
      • 1970-01-01
      • 2012-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-10
      相关资源
      最近更新 更多