Async 10: Switching Threads

It’s time for another look at the upcoming async features in C#. This time it’s different ways to control switching threads.

My preferred way to switch threads is to use the TaskEx.Run() method. You write a lambda expression for the work to be done on a background thread. That syntax nicely scopes the context for the work on the background:

        public async void AsyncRunCPU()
{
Console.WriteLine("On the UI thread.");

int result = await TaskEx.Run(() =>
{
Console.WriteLine
("Starting CPU-intensive work on background thread...");
int work = DoCpuIntensiveWork();
Console.WriteLine("Done with CPU-intensive work!");
return work;
});

Console.WriteLine("Back on the UI thread. Result is {0}.",
result);
}
The new async code provides another way to switch threads: SynchronizationContext.SwitchTo(), and Dispatcher.SwitchTo(). Using these methods, you switch to a background thread or to the foreground thread, respectively.
The advantage is that the code reads a bit more naturally. You can read the algorithm linearly, including the portions that run on the background thread.
 
        public async void AsyncSwitchToCPU()
{
Console.WriteLine("On the UI thread.");

// Switch to a thread pool thread
await new SynchronizationContext().SwitchTo();

Console.WriteLine("Starting CPU-intensive work on background thread...");
int result = DoCpuIntensiveWork();
Console.WriteLine("Done with CPU-intensive work!");

// Switch back to UI thread
await Dispatcher.SwitchTo();

Console.WriteLine("Back on the UI thread. Result is {0}.", result);
}

In this example, its clear and easy to read when the thread contexts change. However, those calls to SynchronizationContext().SwitchTo() and Dispatcher.SwitchTo() might be in different methods. I think that would get confusing in a large codebase.

One other use of SynchronizationContext.SwitchTo() is that you can use it to switch the context to a background thread before starting several background operations. Doing so avoids many more context switches, as each async operation finishes:
 
        public async void AsyncSwitchToThreadPool()
{
Uri[] uris = {
new Uri("http://www.weather.gov"),
new Uri("http://www.weather.gov/climate/"),
new Uri("http://www.weather.gov/rss/") };

// Avoid hops after each async completion
await new SynchronizationContext().SwitchTo();

int totalLength = 0;
foreach (var uri in uris)
{
string s = await new WebClient().DownloadStringTaskAsync(uri);
totalLength += s.Length;
}

// Switch back to UI thread
await Dispatcher.SwitchTo();

Console.WriteLine("Back on the UI thread. Total length of pages is {0}.",
totalLength);
}

The above sample switches execution to a background thread before starting the first network request. That means all three network requests are on background threads, and this code avoids the context switch back to the foreground as each request finishes.
Created: 4/13/2011 5:42:59 PM

Current Projects

I create content for .NET Core. My work appears in the .NET Core documentation site. I'm primarily responsible for the section that will help you learn C#.

All of these projects are Open Source (using the Creative Commons license for content, and the MIT license for code). If you would like to contribute, visit our GitHub Repository. Or, if you have questions, comments, or ideas for improvement, please create an issue for us.

I'm also the president of Humanitarian Toolbox. We build Open Source software that supports Humanitarian Disaster Relief efforts. We'd appreciate any help you can give to our projects. Look at our GitHub home page to see a list of our current projects. See what interests you, and dive in.

Or, if you have a group of volunteers, talk to us about hosting a codeathon event.