Резервные копии задач. Run и UI
Этот фрагмент кода находится в блоге Стивена Клири и дает пример того, как сообщать о прогрессе при использовании Task.Run. Я хотел бы знать, почему нет проблем с перекрестными потоками при обновлении пользовательского интерфейса, и я имею в виду, почему вызов не требуется?
private async void button2_Click(object sender, EventArgs e)
{
var progressHandler = new Progress<string>(value =>
{
label2.Text = value;
});
var progress = progressHandler as IProgress<string>;
await Task.Run(() =>
{
for (int i = 0; i != 100; ++i)
{
if (progress != null)
progress.Report("Stage " + i);
Thread.Sleep(100);
}
});
label2.Text = "Completed.";
}
Ответы
Ответ 1
Progress<T>
захватывает текущий SynchronisationContext
при его создании. Всякий раз, когда вы вызываете Report
, он тайно делегирует это в захваченный контекст. В этом примере захваченным контекстом является пользовательский интерфейс, что означает, что исключений не происходит.
Ответ 2
Конструктор Progress<T>
фиксирует текущий объект SynchronizationContext
.
Класс SynchronizationContext
- это средство, которое абстрагирует детали задействованной модели потоков. То есть, в Windows Forms он будет использовать Control.Invoke
, в WPF он будет использовать Dispatcher.Invoke
и т.д.
Когда вызывается объект progress.Report
, сам объект Progress
знает, что он должен запускать свой делегат, используя захваченный SynchronizationContext
.
В других терминах это работает, потому что Progress
был разработан для обработки этого, если разработчик не должен явно говорить об этом.
Ответ 3
Кажется, вы сбиты с толку из-за того, что часть этого поперечного механизма скрыта от глаз разработчика, поэтому вам просто нужно "взять и использовать": http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx
Мы ввели интерфейс IProgress, чтобы вы могли создать опыт для отображения прогресса. Этот интерфейс предоставляет отчет (T) метод, который асинхронная задача вызывает для отчета о прогрессе. Вы раскрываете это интерфейс в сигнатуре асинхронного метода, а вызывающий предоставить объект, реализующий этот интерфейс. Вместе задача и вызывающий пользователь создает очень полезную ссылку (и может работать на разные потоки).
Мы также предоставили класс Progress, который представляет собой реализацию IProgress. Вам предлагается использовать Прогресс в вашем реализации, поскольку он обрабатывает всю бухгалтерию с сохранением и восстановление контекста синхронизации. Прогресс раскрывает как событие и обратный вызов Action, которые вызывается, когда задача сообщает прогресс. Этот шаблон позволяет вам писать код, который просто реагирует на изменения прогресса по мере их возникновения. Вместе, IProgress и Прогресс обеспечивает простой способ передачи информации о ходе работы с фоновая задача для потока пользовательского интерфейса.
Еще одно замечание: уведомление о ходе выполнения будет вызвано после, часть выполнения задания не только в этот момент. Итак, если ваш поток пользовательского интерфейса работает на холостом ходу, и у вас есть запасной процессорный ядро, задержка будет почти нулевой. Если ваш поток пользовательского интерфейса занят, уведомление не будет вызываться до тех пор, пока поток пользовательского интерфейса не вернется в режим ожидания (независимо от того, сколько резервных ядер процессора имеет ваш компьютер).