Я настоятельно рекомендую вам прочитать документ Asynchronous Pattern на основе задач.Это позволит вам структурировать свои API-интерфейсы, чтобы они были готовы к выходу на улицу async
и await
.
Раньше я использовал TaskScheduler
для очереди обновлений, аналогично вашему решению ( сообщение в блоге)), но я больше не рекомендую такой подход.
Документ TAP имеет простое решение, которое более элегантно решает проблему: если фоновая операция хочет выдавать отчеты о ходе выполнения, она принимает аргумент типаIProgress<T>
:
public interface IProgress<in T> { void Report(T value); }
В этом случае базовую реализацию относительно просто предоставить:
public sealed class EventProgress<T> : IProgress<T>
{
private readonly SynchronizationContext syncContext;
public EventProgress()
{
this.syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
}
public event Action<T> Progress;
void IProgress<T>.Report(T value)
{
this.syncContext.Post(_ =>
{
if (this.Progress != null)
this.Progress(value);
}, null);
}
}
(SynchronizationContext.Current
по сути TaskScheduler.FromCurrentSynchronizationContext
без необходимости фактических Task
с).
Async CTP содержит IProgress<T>
и тип Progress<T>
, который аналогичен EventProgress<T>
выше (но более производительный).Если вы не хотите устанавливать вещи уровня CTP, то вы можете просто использовать приведенные выше типы.
Подводя итог, на самом деле есть четыре варианта:
IProgress<T>
-именно так в будущем будет написан асинхронный код.Это также вынуждает вас отделять фоновую логику работы от вашего кода обновления пользовательского интерфейса / ViewModel , что хорошо. TaskScheduler
- неплохой подход;это то, что я использовал долгое время перед переключением на IProgress<T>
.Однако он не вытесняет код обновления пользовательского интерфейса / ViewModel из логики фоновых операций. SynchronizationContext
- те же преимущества и недостатки, что и у TaskScheduler
, через менее известный API . Dispatcher
- действительно может не рекомендовать это!Рассмотрим фоновые операции по обновлению ViewModel - так что в коде прогресса обновления нет ничего специфичного для пользовательского интерфейса.В этом случае, используя Dispatcher
, просто привязали вашу ViewModel к вашей платформе пользовательского интерфейса.Nasty.
PS Если вы решите использовать Async CTP, у меня есть несколько дополнительных IProgress<T>
реализаций в моей библиотеке Nito.AsyncEx , включая одну (PropertyProgress
) отправляет отчеты о ходе выполнения через INotifyPropertyChanged
(после переключения обратно в поток пользовательского интерфейса через SynchronizationContext
).