2010-12-02 4 views
174

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

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

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

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

В случае вашего примера вы можете использовать `Control.Invoke (Action)`, т.е. `TextBlock1.Invoke`, а не` dispatcher.Invoke` – 2016-07-12 11:11:02

+0

Спасибо @ColonelPanic, но я использовал WPF (как помечено), а не winforms. – 2016-07-13 03:12:57

ответ

286

Вызов продолжение с TaskScheduler.FromCurrentSynchronizationContext():

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

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

+33

Действует только в том случае, если текущий контекст выполнения находится в потоке пользовательского интерфейса. Если вы поместите этот код в другую задачу, вы получите InvalidOperationException (смотрите [Исключения] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.fromcurrentsynchronizationcontext (v = vs. 110) .aspx)) – stukselbax 2014-06-19 08:41:48

+0

В .NET 4.5 Ответ Johan Larsson должен использоваться как стандартный способ продолжения задачи в потоке пользовательского интерфейса. Просто напишите: ждут Task.Run (DoLongRunningWork); this.TextBlock1.Text = "Complete"; См. Также: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx – 2016-07-15 09:32:56

18

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

Это который вызывается из 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()); 
+0

Я угадываю = перед тем, как GenerateManifest является опечаткой. – 2015-10-19 13:10:50

9

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

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

С асинхронной вы просто сделать:

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. 
0

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

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

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

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