如何让 BackgroundWorker ProgressChanged 事件按顺序执行?
Posted
技术标签:
【中文标题】如何让 BackgroundWorker ProgressChanged 事件按顺序执行?【英文标题】:How to make BackgroundWorker ProgressChanged events execute in sequence? 【发布时间】:2021-03-03 20:32:07 【问题描述】:考虑以下代码:
private static BackgroundWorker bg = new BackgroundWorker();
static void Main(string[] args)
bg.DoWork += bg_DoWork;
bg.ProgressChanged += bg_ProgressChanged;
bg.WorkerReportsProgress = true;
bg.RunWorkerAsync();
Thread.Sleep(10000);
static void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
Console.WriteLine(e.ProgressPercentage);
Thread.Sleep(100);
Console.WriteLine(e.ProgressPercentage);
static void bg_DoWork(object sender, DoWorkEventArgs e)
for (int i = 0; i < 10; i++)
bg.ReportProgress(i);
运行时我得到以下输出:
0 1 1 2 0 3 2 3 5 4 4 6 5 7 7 8 6 9 8 9
我了解问题是 BackgroundWorker 为每次调用 ReportProgress 启动的线程之间的竞争条件。
如何确保每个 bg_ProgressChanged 的整个主体都按照我调用它们的顺序执行? 那就是我想得到 p>
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
结果。
【问题讨论】:
BackgroundWorker 并不是真的要在没有 UI 线程的情况下使用。 在增量中优先使用 ReportProgress 的 Ist 参数,而不是 userState 参数。建议您查看此 MSDN 链接以报告进度:msdn.microsoft.com/en-us/library/a3zbdb1t.aspx @Vijay 谢谢。修好了。 尝试将 ReportProgress 方法调用包装在 lock(_objectLock) 块中,例如lock(_lockObject) bg.ReportProgress(i); @Vijay:这根本不会有任何区别;这些调用在同一个线程上运行。 【参考方案1】:BackgroundWorker
在调用 RunWorkerAsync()
的线程的当前 SynchronizationContext 上引发 ProgressChanged
事件。
默认的 SynchronizationContext 在 ThreadPool 上运行回调而不进行任何同步。
如果您在 UI 应用程序(WPF 或 WinForms)中使用 BackgroundWorker,它将使用该 UI 平台的 SynchronizationContext,它将按顺序执行回调。
【讨论】:
我已将示例创建为目标 .NET Framework 3.5 的控制台应用程序。我已将输出类型更改为 Windows 应用程序,它按照您的描述工作。 我的问题是我希望 ProgressChanged 事件的整个主体按照我调用它们的顺序执行。我已经更新了这个问题,以更好地反映我想做的事情。 @Andris:在 UI SynchronizationContext 下,它们将按顺序执行。 @SLaks:正如我所说,我已将应用程序输出类型更改为 Windows 应用程序,但请注意,在我修改后的示例的输出中,前 5 个在前 4 个之前,所以调用参数 5 在参数 4 之前执行。更改输出类型还不够吗?我不确定如何将 SynchronizationContext 设置为“UI SynchronizationContext”。我不熟悉该主题。 你需要调用Application.Run()
来运行一个UI线程。 (这是一个阻塞调用)。项目类型根本不重要。然而,如果你不做 UI,你可能根本不应该使用 BackgroundWorker。【参考方案2】:
不要使用这个解决方案!!!正如 SLaks 指出的那样,可能会导致死锁。
我似乎偶然发现了一个答案。我通过以下方式更改了代码:
[MethodImpl(MethodImplOptions.Synchronized)]
static void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
Console.WriteLine(e.ProgressPercentage);
Thread.Sleep(100);
Console.WriteLine(e.ProgressPercentage);
现在我得到了我想要的输出:
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
【讨论】:
在 UI 应用程序中,这没有什么区别,因为回调只会在 UI 线程上运行。 请注意,这可能会导致死锁。[MethodImpl(MethodImplOptions.Synchronized)]
等价于 lock(this)
,不应使用。
@SLaks:很好的提示。再次感谢。以上是关于如何让 BackgroundWorker ProgressChanged 事件按顺序执行?的主要内容,如果未能解决你的问题,请参考以下文章
是不是 .NET 4.0 TPL 让 APM、EAP 和 BackgroundWorker 异步模式过时了?
如何在点击触发的backgroundWorker中添加一段代码?
如何在窗体的关闭事件中停止 BackgroundWorker?