我有一个MVVM应用程序,它使用.NET 4.0任务并行库在后台处理大量图像。处理在后台线程上完成,后台线程将其进度发布到绑定到进度对话框的视图模型属性。那部分工作正常。但后台线程还需要在完成所有处理后通知主线程,以便可以关闭进度对话框。
这就是我的困境:使用我当前的代码,一旦设置了后台任务,'处理结束'语句就会被命中。但是如果我在两者之间插入task.Wait()
语句,它似乎会阻止UI线程,从而阻止进度更新。那么,后台线程如何向主线程发出信号完成?谢谢你的帮助。
以下是创建后台任务的UI线程上的代码:
/* The view should subscribe to the two events referenced below, to
* show and close a progress indicator, such as a Progress dialog. */
// Announce that image processing is starting
m_ViewModel.RaiseImageProcessingStartingEvent();
// Process files as a background task
var task = Task.Factory.StartNew(() => DoWork(fileList, progressDialogViewModel));
// Announce that image processing is finished
m_ViewModel.RaiseImageProcessingEndingEvent();
这是后台线程上的DoWork()
方法。它使用Parallel.ForEach()
语句处理图像文件:
private void DoWork(IEnumerable<string> fileList, ProgressDialogViewModel viewModel)
{
// Declare local counter
var completedCount = 0;
// Process images in parallel
Parallel.ForEach(fileList, imagePath =>
{
ProcessImage(imagePath);
Interlocked.Increment(ref completedCount);
viewModel.Progress = completedCount;
});
}
答案 0 :(得分:2)
您需要在后台任务中添加一个续集,该后台任务将在当前Dispatcher
(SynchronizationContext
)线程上触发。这看起来像这样:
task.ContinueWith(t =>
{
// This will fire on the Dispatcher thread
m_ViewModel.RaiseImageProcessingEndingEvent();
},
TaskScheduler.FromCurrentSynchronizationContext());
使用指定ContinueWith
的{{1}}的重载,更具体地说,使用TaskScheduler
确保您的连续逻辑将在WPF Dispatcher线程上触发,在该线程中可以安全地访问UI元件。
那就是说,我也应该指出你可能不想修改viewModel.Progress属性,因为如果UI元素绑定到它,它们将从后台线程更新,这不是goo 。相反,你应该在那里分离一个任务,以确保通知在正确的线程上传播。但这样做的诀窍是你的DoWork在启动它时不知道当前的同步上下文,因此你需要在启动原始任务时将其传入。
更新:请参阅我的后续评论,了解我为何更新进度属性的最后一段。