Task continuation on UI thread

C#.NetWpfMultithreadingTask

C# Problem Overview


Is there a 'standard' way to specify that a task continuation should run on the thread from which the initial task was created?

Currently I have the code below - it is working but keeping track of the dispatcher and creating a second Action seems like unnecessary overhead.

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

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

C# Solutions


Solution 1 - C#

Call the continuation with TaskScheduler.FromCurrentSynchronizationContext():

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

This is suitable only if the current execution context is on the UI thread.

Solution 2 - C#

With async you just do:

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.

Solution 3 - C#

If you have a return value you need to send to the UI you can use the generic version like this:

This is being called from an MVVM ViewModel in my case.

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());

Solution 4 - C#

I just wanted to add this version because this is such a useful thread and I think this is a very simple implementation. I have used this multiple times in various types if multithreaded application:

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

Solution 5 - C#

Got here through google because i was looking for a good way to do things on the ui thread after being inside a Task.Run call - Using the following code you can use await to get back to the UI Thread again.

I hope this helps 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) ...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGreg SansomView Question on Stackoverflow
Solution 1 - C#Greg SansomView Answer on Stackoverflow
Solution 2 - C#Johan LarssonView Answer on Stackoverflow
Solution 3 - C#Simon_WeaverView Answer on Stackoverflow
Solution 4 - C#DeanView Answer on Stackoverflow
Solution 5 - C#DblView Answer on Stackoverflow