Продолжение задачи в потоке пользовательского интерфейса

Существует ли "стандартный" способ указать, что продолжение задачи должно выполняться в потоке, из которого была создана первоначальная задача?

В настоящее время у меня есть код ниже - он работает, но отслеживание диспетчера и создание второго действия кажется ненужными накладными расходами.

dispatcher = Dispatcher.CurrentDispatcher;
Task task = Task.Factory.StartNew(() =>
{
    DoLongRunningWork();
});

Task UITask= task.ContinueWith(() =>
{
    dispatcher.Invoke(new Action(() =>
    {
        this.TextBlock1.Text = "Complete"; 
    }
});

4 ответа

Решение

Назовите продолжение с TaskScheduler.FromCurrentSynchronizationContext():

    Task UITask= task.ContinueWith(() =>
    {
     this.TextBlock1.Text = "Complete"; 
    }, TaskScheduler.FromCurrentSynchronizationContext());

Это подходит, только если текущий контекст выполнения находится в потоке пользовательского интерфейса.

С помощью 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.

Если у вас есть возвращаемое значение, которое вам нужно отправить в пользовательский интерфейс, вы можете использовать общую версию, например:

В моем случае это вызывается из MVVM ViewModel.

var updateManifest = Task<ShippingManifest>.Run(() =>
    {
        Thread.Sleep(5000);  // prove it's 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());

Я просто хотел добавить эту версию, потому что это такая полезная тема, и я думаю, что это очень простая реализация. Я использовал это несколько раз в различных типах многопоточных приложений:

 Task.Factory.StartNew(() =>
      {
        DoLongRunningWork();
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
              { txt.Text = "Complete"; }));
      });

Попал сюда через Google, потому что я искал хороший способ делать что-то в потоке пользовательского интерфейса после того, как был внутри вызова Task.Run. Используя следующий код, вы можете использовать await чтобы снова вернуться к потоку пользовательского интерфейса.

Я надеюсь, что это помогает кому-то.

public static class UI
{
    public static DispatcherAwaiter Thread => new DispatcherAwaiter();
}

public struct DispatcherAwaiter : INotifyCompletion
{
    public bool IsCompleted => Application.Current.Dispatcher.CheckAccess();

    public void OnCompleted(Action continuation) => Application.Current.Dispatcher.Invoke(continuation);

    public void GetResult() { }

    public DispatcherAwaiter GetAwaiter()
    {
        return this;
    }
}

Применение:

... code which is executed on the background thread...
await UI.Thread;
... code which will be run in the application dispatcher (ui thread) ...

Просто напишите свой код как (Но используяContinueWithэто хорошая практика, не беспокойтесь о ненужных накладных расходах во время выполнения)

 Task task = Task.Factory.StartNew(() =>
{
    DoLongRunningWork();
    Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
    {
        this.TextBlock1.Text = "Complete"; 
    }
});

Положил Dispatcher код в finally заблокировать, если вы хотите убедиться, что это работает.

Попробуйте избежать TaskScheduler.FromCurrentSynchronizationContext() как с помощью этого вашего интерфейса Thread может быть заблокирован вашим текущим Thread,

Другие вопросы по тегам