BackgroundWorker 是 .NET Framework 提供的一个类,用于在后台线程上执行长时间运行的任务,同时可以安全地与UI线程进行交互。在WPF应用程序中,BackgroundWorker 通常用于异步执行任务,以避免阻塞UI线程。然而,BackgroundWorker 的行为在某些情况下可能会令人困惑,特别是当它与UI线程交互时。本文将深入探讨BackgroundWorker 的工作原理,以及它在WPF应用程序中的使用方式。
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个或更多的线程:
如果在即将成为UI线程的线程上运行BackgroundWorker,例如在调用 Application.Run() 之前的应用主线程,Windows并不知道即将要做什么。因此,在实际启动WPF应用程序之前,主线程被认为是一个常规线程,并且会使用默认的同步上下文,而不是 DispatcherSynchronizationContext。因此,后台工作事件不会被回传到UI线程,而是会在线程池上执行。如果在这些回调中触摸UI,将会发生不好的事情。