开发者

TPL: Background thread completion notification?

I have an MVVM application that processes a large number of images in the background, using the .NET 4.0 Task Parrallel Library. The processing is done on a background thread, which posts its progress to a view model property that is bound to a progress dialog. That part is working okay. But the background thread also needs to notify the main thread when all processing is done, so that the progress dialog can be closed.

Here's my dilemma: With my current code, the 'processing ending' statement gets hit as so开发者_StackOverflow中文版on as the background task is set up. But if I insert a task.Wait() statement between the two, it appears to block the UI thread, preventing progress updates. So, how does the background thread signal completion to the main thread? Thanks for your help.


Here is the code on the UI thread that creates the background task:

/* 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();

And here is the DoWork() method on the background thread. It processes the image files, using a Parallel.ForEach() statement:

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; 
        });
}


You need to add a continuation to the background task which will fire on the current Dispatcher (SynchronizationContext) thread. That would look like this:

task.ContinueWith(t =>
{
   // This will fire on the Dispatcher thread
   m_ViewModel.RaiseImageProcessingEndingEvent();
},
TaskScheduler.FromCurrentSynchronizationContext());

Using the overload of ContinueWith that specifies the TaskScheduler and, more specifically, using FromCurrentSynchronizationContext ensures that your continuation logic will fire on the WPF Dispatcher thread where it is safe to access UI elements.

That said, I should also point out that you probably don't want to be modifying viewModel.Progress property because, if UI elements are bound to it, they will be updated from a background thread which is no goo. Instead you should spin off a task inside there as well to ensure the notification is propagated on the correct thread. The trick to that though is that your DoWork doesn't know the current synchronization context at the point where it's launched, so you would need to pass it in when you kick off the original task.

UPDATE: see my follow up comment for why I struck my last paragraph on updating the progress property.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜