深入理解BackgroundWorker及其在WPF中的应用

BackgroundWorker 是 .NET Framework 提供的一个类,用于在后台线程上执行长时间运行的任务,同时可以安全地与UI线程进行交互。在WPF应用程序中,BackgroundWorker 通常用于异步执行任务,以避免阻塞UI线程。然而,BackgroundWorker 的行为在某些情况下可能会令人困惑,特别是当它与UI线程交互时。本文将深入探讨BackgroundWorker 的工作原理,以及它在WPF应用程序中的使用方式。

BackgroundWorker 的工作原理

BackgroundWorker 使用 SynchronizationContext.Post() 方法来将进度和完成事件回传给调用线程。当调用 RunWorkerAsync() 方法时,它会捕获当前线程的同步上下文。如果当前线程没有同步上下文,它将创建一个默认的同步上下文。对于WPFUI线程,同步上下文(DispatcherSynchronizationContext 类型)确实会将事件回传给UI线程。

在UI线程上,RunWorkerAsync() 方法会启动一个后台线程来执行 DoWork() 方法。当 DoWork() 方法完成后,RunWorkerCompleted 和 ProgressChanged 事件将被触发。这些事件默认情况下会被调度回UI线程,因为它们是在UI线程上创建的BackgroundWorker

// 示例代码 BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += (sender, e) => { // 执行长时间运行的任务 }; worker.RunWorkerCompleted += (sender, e) => { // 当任务完成时更新UI }; worker.ProgressChanged += (sender, e) => { // 更新进度 }; worker.RunWorkerAsync();

在后台线程或控制台应用程序的主线程中,同步上下文会将事件调用安排在线程池上。因此,通常情况下,会看到3个或更多的线程:

  • 调用线程:调用 RunWorkerAsync()。
  • 线程池线程 #1:执行 DoWork()。
  • 线程池线程 #2...n:触发 RunWorkerCompleted 和 ProgressChanged事件

在即将成为UI线程的线程上运行BackgroundWorker

如果在即将成为UI线程的线程上运行BackgroundWorker,例如在调用 Application.Run() 之前的应用主线程,Windows并不知道即将要做什么。因此,在实际启动WPF应用程序之前,主线程被认为是一个常规线程,并且会使用默认的同步上下文,而不是 DispatcherSynchronizationContext。因此,后台工作事件不会被回传到UI线程,而是会在线程池上执行。如果在这些回调中触摸UI,将会发生不好的事情。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485