TPL: фоновое уведомление о завершении потока? - PullRequest
1 голос
/ 01 сентября 2011

У меня есть приложение MVVM, которое обрабатывает большое количество изображений в фоновом режиме, используя параллельную библиотеку задач .NET 4.0.Обработка выполняется в фоновом потоке, который публикует свой прогресс в свойстве модели представления, которое привязано к диалогу прогресса.Эта часть работает хорошо.Но фоновый поток также должен уведомить основной поток, когда вся обработка завершена, чтобы диалоговое окно хода выполнения могло быть закрыто.

Вот моя дилемма: в моем текущем коде выражение «окончание обработки» выполняется каккак только фоновая задача настроена.Но если я вставлю оператор task.Wait() между ними, он, похоже, заблокирует поток пользовательского интерфейса, предотвращая обновления прогресса.Итак, как фоновый поток сигнализирует о завершении основного потока?Спасибо за вашу помощь.


Вот код в потоке пользовательского интерфейса, который создает фоновую задачу:

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

1 Ответ

2 голосов
/ 01 сентября 2011

Вам необходимо добавить продолжение к фоновой задаче, которая будет запускаться в текущем потоке Dispatcher (SynchronizationContext).Это будет выглядеть так:

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

Использование перегрузки ContinueWith, которая задает TaskScheduler, и, более конкретно, использование FromCurrentSynchronizationContext гарантирует, что ваша логика продолжения сработает в потоке диспетчера WPF, гдедоступ к элементам пользовательского интерфейса безопасен.

Тем не менее, я должен также отметить, что вы, вероятно, не хотите изменять свойство viewModel.Progress, потому что, если к нему привязаны элементы пользовательского интерфейса, онибудет обновляться из фоновой темы, которая не является Goo.Вместо этого вы должны также выполнить задачу внутри, чтобы гарантировать, что уведомление распространяется в правильном потоке.Однако хитрость заключается в том, что ваш DoWork не знает текущий контекст синхронизации в том месте, где он запущен, поэтому вам нужно будет передать его при запуске исходного задания.

ОБНОВЛЕНИЕ: см. Мой комментарий о том, почему я выполнил последний абзац по обновлению свойства прогресса.

...