Продолжение задачи в потоке пользовательского интерфейса
Существует ли "стандартный" способ указать, что продолжение задачи должно выполняться в потоке, из которого была создана начальная задача?
В настоящее время у меня есть код ниже - он работает, но отслеживание диспетчера и создание второго действия кажется ненужным накладными расходами.
dispatcher = Dispatcher.CurrentDispatcher;
Task task = Task.Factory.StartNew(() =>
{
DoLongRunningWork();
});
Task UITask= task.ContinueWith(() =>
{
dispatcher.Invoke(new Action(() =>
{
this.TextBlock1.Text = "Complete";
}
});
Ответы
Ответ 1
Вызвать продолжение с помощью TaskScheduler.FromCurrentSynchronizationContext()
:
Task UITask= task.ContinueWith(() =>
{
this.TextBlock1.Text = "Complete";
}, TaskScheduler.FromCurrentSynchronizationContext());
Это подходит только в том случае, если текущий контекст выполнения находится в потоке пользовательского интерфейса.
Ответ 2
С помощью async вы просто выполните:
await Task.Run(() => do some stuff);
// continue doing stuff on the same context as before.
// while it is the default it is nice to be explicit about it with:
await Task.Run(() => do some stuff).ConfigureAwait(true);
Однако:
await Task.Run(() => do some stuff).ConfigureAwait(false);
// continue doing stuff on the same thread as the task finished on.
Ответ 3
Если у вас есть возвращаемое значение, которое необходимо отправить в пользовательский интерфейс, вы можете использовать общую версию следующим образом:
Это вызвано из MVVM ViewModel в моем случае.
var updateManifest = Task<ShippingManifest>.Run(() =>
{
Thread.Sleep(5000); // prove it really working!
// GenerateManifest calls service and returns 'ShippingManifest' object
return GenerateManifest();
})
.ContinueWith(manifest =>
{
// MVVM property
this.ShippingManifest = manifest.Result;
// or if you are not using MVVM...
// txtShippingManifest.Text = manifest.Result.ToString();
System.Diagnostics.Debug.WriteLine("UI manifest updated - " + DateTime.Now);
}, TaskScheduler.FromCurrentSynchronizationContext());
Ответ 4
Я просто хотел добавить эту версию, потому что это такой полезный поток, и я думаю, что это очень простая реализация. Я использовал это несколько раз в разных типах, если многопоточное приложение:
Task.Factory.StartNew(() =>
{
DoLongRunningWork();
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{ txt.Text = "Complete"; }));
});
Ответ 5
J ust напишите свой код как (но использование ContinueWith
- хорошая практика, не беспокойтесь о ненужных накладных расходах для времени выполнения)
Task task = Task.Factory.StartNew(() =>
{
DoLongRunningWork();
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
this.TextBlock1.Text = "Complete";
}
});
Поместите Dispatcher
код в блок finally
, если вы хотите убедиться, что это для запуска.
Попробуйте избежать TaskScheduler.FromCurrentSynchronizationContext()
, так как от этого пользовательский интерфейс Thread
может быть заблокирован вашим текущим Thread
.