Home

Code Beauty : C# Async and Threading

February 17, 2013

There is one beautiful thing with the current .NET Framework and Windows 8 Apps : running your code asynchronously without blocking your UI thread.

You simply have to have a function like that, and it “magically” works:

private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    try
    {
         textBox1.Text = 
         await httpClient.GetStringAsync
         ("http://msdn.microsoft.com");
    } catch (Exception) { // Process the exception. . . . }
}

Using that, you get a code that is running from the UI thread, runs into StartButton_Click, launch a task for the GetStringAsync and returns. When the GetStringAsync is finally over, the “textBox1.Text = thing” part get executed, and it works because we are still on the UI Thread. Doing so, we don’t violate the constraint of accessing UI elements from the thread that created it.

But how does it work really ? How does the Framework knows which thread is targeted and where to run the remaining of the method ?

The simplest way is to create a new console application from scratch and doing async work here:

        static void Main(string[] args)
        {
            AsyncWork();

            while (true)
            {
                // Hang on there
                Thread.Sleep(500);
            }
        }

        static async Task AsyncWork()
        {
            Console.WriteLine("Async thread : {0}", Thread.CurrentThread.ManagedThreadId);

            while (true)
            {
                await Task.Run(() =>
                    {
                        Console.WriteLine("Worker : {0}", Thread.CurrentThread.ManagedThreadId);
                        Thread.Sleep(500);
                    });

                Console.WriteLine("Async thread : {0}", Thread.CurrentThread.ManagedThreadId);
            }
        }

I simply display the thread that is running my Async Method, and the thread that is doing the real loading work.
AsyncDemoNotWorking

Wait! This is wrong!

I got my async method running, but each time I display “Async thread”, the thread id is different. If a look into VS I also see that the thread running my async method is a “Worker Thread”, not the thread that was active when launching my application. If I was running an UI or some work with thread affinity, I might have gotten a crash because of that.

So what’s the thing that a WinForms, Windows 8 or WPF application has that I don’t have? A loop serving for the UI. And that loop is implemented using a SynchronizationContext in the Framework. When a Dispatcher.Invoke is made or an async method returns, it get posted via the SynchronizationContext to the correct thread before running.

If we look at this blog post from the Framework Team, the magic is here. We then just have to create a SynchronizationContext for our Console App capable of running back our code on the same thread:

    class Program
    {
        static void Main(string[] args)
        {
            Encapsulate(() => AsyncWork());

            while (true)
            {
                // Hang on there
                Thread.Sleep(500);
            }
        }

        static async Task AsyncWork()
        {
            Console.WriteLine("Async thread : {0}", Thread.CurrentThread.ManagedThreadId);

            while (true)
            {
                await Task.Run(() =>
                    {
                        Console.WriteLine("Worker : {0}", Thread.CurrentThread.ManagedThreadId);
                        Thread.Sleep(500);
                    });

                Console.WriteLine("Async thread : {0}", Thread.CurrentThread.ManagedThreadId);
            }
        }

        static void Encapsulate(Func<Task> action)
        {
            var prevCtx = SynchronizationContext.Current;
            try
            {
                var syncCtx = new SingleThreadSynchronizationContext();
                SynchronizationContext.SetSynchronizationContext(syncCtx);

                var task = action();
                
                task.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

                syncCtx.RunOnCurrentThread();

                task.GetAwaiter().GetResult();
            }
            finally
            {
                SynchronizationContext.SetSynchronizationContext(prevCtx);
            }
        }

        private sealed class SingleThreadSynchronizationContext :  
            SynchronizationContext
        {
            private readonly BlockingCollection<KeyValuePair<SendOrPostCallback,object>> m_queue =
               new BlockingCollection<KeyValuePair<SendOrPostCallback,object>>();
 
            public override void Post(SendOrPostCallback d, object state)
            {
                m_queue.Add(new KeyValuePair<SendOrPostCallback,object>(d, state));
            }
 
            public void RunOnCurrentThread()
            {
                KeyValuePair<SendOrPostCallback, object> workItem;

                while(m_queue.TryTake(out workItem, Timeout.Infinite))
                    workItem.Key(workItem.Value);
            }

            public void Complete()
            {
                m_queue.CompleteAdding();
            }
        }
    }

AsyncDemoWorking

If you look at the sample application, we now have the Async thread always running on thread 9. All we had to do was to wrap our async work into a SynchronizationContext.

We are good πŸ™‚

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: