Problem
Is there a’standard’ way to tell a task continuation that it should execute on the same thread as the original task?
I now have the code below, which works, but I don’t think maintaining track of the dispatcher and creating a second Action is essential.
dispatcher = Dispatcher.CurrentDispatcher;
Task task = Task.Factory.StartNew(() =>
{
DoLongRunningWork();
});
Task UITask= task.ContinueWith(() =>
{
dispatcher.Invoke(new Action(() =>
{
this.TextBlock1.Text = "Complete";
}
});
Asked by Greg Sansom
Solution #1
TaskScheduler is used to call the continuation. FromCurrentSynchronizationContext():
Task UITask= task.ContinueWith(() =>
{
this.TextBlock1.Text = "Complete";
}, TaskScheduler.FromCurrentSynchronizationContext());
Only if the current execution context is on the UI thread is this appropriate.
Answered by Greg Sansom
Solution #2
With async, all you have to do is:
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);
However:
await Task.Run(() => do some stuff).ConfigureAwait(false);
// continue doing stuff on the same thread as the task finished on.
Answered by Johan Larsson
Solution #3
If you need to transmit a return value to the user interface, you can use the generic version, which looks like this:
In my example, this is being called from an 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());
Answered by Simon_Weaver
Solution #4
I just wanted to add this version because I think it’s a pretty simple implementation and this is such a great discussion. I’ve used this in a variety of multithreaded applications, including:
Task.Factory.StartNew(() =>
{
DoLongRunningWork();
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{ txt.Text = "Complete"; }));
});
Answered by Dean
Solution #5
I found this site via Google when looking for a nice way to conduct stuff on the ui thread after being inside a Task. Run call – You may use await to return to the UI Thread using the following code.
I hope this information is useful to someone.
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;
}
}
Usage:
... code which is executed on the background thread...
await UI.Thread;
... code which will be run in the application dispatcher (ui thread) ...
Answered by Dbl
Post is based on https://stackoverflow.com/questions/4331262/task-continuation-on-ui-thread