【问题标题】:Thread sleeping in a BackgroundWorker在 BackgroundWorker 中休眠的线程
【发布时间】:2015-06-10 19:46:24
【问题描述】:

我编写了一个简单的应用程序,它使用 BackgroundWorker 将 100000 行“Hello World”添加到列表中。

下面是我的后台工作人员在一个单独的线程中所做的工作的代码:

    private void BgWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
    {
        int min = 0;

        foreach (var hw in hwList)
        {
            //new ManualResetEvent(false).WaitOne(1);
            Thread.Sleep(1);
            int progress = Convert.ToInt32((Double)min / hwList.Count * 100);
            min++;
            bgWorker.ReportProgress(progress);
        }
    }

    // Updating the progress
    private void BgWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
    {
        ProgressBar.Value = progressChangedEventArgs.ProgressPercentage;
    }

一切正常,除了如果我删除 Thread.Sleep(1) BackgroundWorker 不再报告进度。 (我想它需要一些时间)。将线程暂停 1 毫秒实际上会使 BackgroundWorker 报告进度,但速度很慢。

我的问题是,有没有一种方法可以摆脱线程休眠但同时让 BackgroundWorker 正确报告进度?

据我了解,暂停 BackgroundWorker 是不可避免的,因为线程需要一些时间来执行任务,但我想知道是否有解决方法。

【问题讨论】:

  • 是什么让您认为它在快速/不睡觉时没有报告进度?
  • 这段代码在做什么?它看起来像一个非常简单的 for 循环 - 报告进度可能比循环的其余部分要昂贵得多。
  • “BackgroundWorker 不再报告进度”您能否详细说明此声明?我相信它仍然会报告,你不只是正确地使用它。
  • 那么这是一个完全不同的问题。也许它发生得太快而无法看到?它最终满了吗?我们需要查看代码才能知道那里发生了什么。
  • @deefrson 由于进度是一个整数,因此您不会看到 2,000,000 循环的第 1 次和第 10,000 次迭代之间有任何区别。两者都是 0。

标签: c# wpf multithreading backgroundworker thread-sleep


【解决方案1】:

我遇到了经常报告进度的问题,而且没有理由报告相同的进度这么多次浪费 CPU 周期。

 private void BgWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
    {
        int min = 0;
        int oldProgress = 0;
        foreach (var hw in hwList)
        {
            // new ManualResetEvent(false).WaitOne(1);
            // Thread.Sleep(1);
            int progress = Convert.ToInt32((Double)min / hwList.Count * 100);
            min++;

            // Only report progress when it changes
            if(progress != oldProgress){
                 bgWorker.ReportProgress(progress);
                 oldProgress = progress;
            }
        }
    }

    // Updating the progress
    private void BgWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
    {
        ProgressBar.Value = progressChangedEventArgs.ProgressPercentage;
    }

【讨论】:

  • 好的,我现在明白了,由于进度是一个整数,我多次报告一些值,所以你的代码应该可以解决问题。事实上,CPU周期被浪费了。我会用你的代码尝试一些东西,如果它有效,我会将你的答案标记为已接受。
  • @deefrson 和 Marko ... 不是 100% 确定,但是当您从后台线程更新 UI 时,您必须在 Dispatcher 线程上进行。 BackgroundWorker 是否促进了这一点?否则,您将收到跨线程异常。
  • @Darek 在这种情况下,您实际上并不需要 Dispatcher,因为 BackgroundWorker 与主线程“同步”。无论如何,我已经阅读了一些关于 .NET 4.0 附带的令人惊叹的 TPL(任务并行库)的非常好的东西,它促进了许多事情。所以这可能是 BackgroundWorker 的替代品。
  • 或者,您可以根据时间更新或根据loopIndex % 10000 == 0 进行节流。 (+1)
  • @usr 我建议不要使用幻数,如果列表的计数发生变化,油门数需要调整/维护。保持简单,仅在进度发生变化时才报告进度,无需限制或使用时间。高效且有弹性的代码无需增加额外的复杂性。
【解决方案2】:

我开发了一个名为 SxProgress 的类,而不是 BackgroundWorker,它有一个非常简单的接口,您可以通过以下方式使用它:

 int DesiredLinesCount = 100000 ;
 List<string> Lines=new List<string>() ;
 object[] UserObjects = new object[] { Lines } ; 
 SxProgress.Execute("Building lines",DesiredLinesCount,true,false,
                    BuildLines_ExecInThread,UserObjects)) ;

 private bool BuildLines_ExecInThread(int ItemIndex,object[] UserObjects)
{
  // some sleep to slow down the process (demonstration purpose only)
  // if (ItemIndex % 10 ==0) System.Threading.Thread.Sleep(1) ; 
  List<string> Lines= (List<String>)UserObjects[0] ;
  Lines.Add("Hello world") ; 
  return true ; 
}

The SxProgress code is in the last message of this link

还要注意,该类为并行处理提供了相同的简单接口,在计算机中创建与内核一样多的线程,并将项目透明地分派到不同的线程。

【讨论】:

    【解决方案3】:

    这适用于 WPF 吗? 不幸的是没有:该类包含一个带有进度条、标签和停止按钮的表单(winforms)。

    可能的解决方案我从未尝试过。

    它可以通过从“添加引用”对话框表单(在“.NET”选项卡中)添加到 WPF 项目 2 引用来工作,即“System.Windows.forms”和“WindowsFormsIntegration”。

    然后,在源代码中,添加“使用 System.Windows.Forms;”
    和“使用 System.Windows.Forms.Integration;”

    【讨论】:

      猜你喜欢
      • 2019-08-12
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 2011-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-12
      相关资源
      最近更新 更多