A Synchronization Context Based On The TPL Dataflow Library

The purpose of the synchronization model implemented by the SynchronizationContext class is to allow the asynchronous/synchronization operations of the common language runtime to behave properly with different synchronization models. This model also simplifies some of the requirements that managed applications have to follow in order to work correctly under different synchronization environments. Examples of such synchronization environments are user interface infrastructures like Windows Forms, Windows Presentation Foundation and ASP.NET.


Providers of synchronization models extend this class to provide their own implementations. Because these user interface infrastructures usually run on their own threads, dispatching execution to their execution contexts usually means leaving the current thread.


The Task Parallel Library (TPL) provides dataflow components to help increase the robustness of concurrency-enabled applications. These dataflow components are collectively referred to as the TPL Dataflow Library. This dataflow model promotes actor-based programming by providing in-process message passing for coarse-grained dataflow and pipelining tasks. The dataflow components build on the types and scheduling infrastructure of the TPL and integrate with the C#, Visual Basic, and F# language support for asynchronous programming. These dataflow components are useful when you have multiple operations that must communicate with one another asynchronously or when you want to process data as it becomes available.


The TPL Dataflow Library (System.Threading.Tasks.Dataflow namespace) is not distributed with the .NET Framework 4.5. To install the System.Threading.Tasks.Dataflow namespace, open your project in Visual Studio 2012, choose Manage NuGet Packages from the Project menu, and search online for the Microsoft.Tpl.Dataflow package.


Sometimes, for demo or testing purposes, I need a synchronization context that behaves like the user interface ones but doesn’t force me to build applications with a user interface and the TPL Dataflow Library seemed like a good option to implement such synchronization context.


It was as easy as this:


public class TplDataflowSynchronizationContext : SynchronizationContext
{
    private ActionBlock<ActionItem> ab
        = new ActionBlock<ActionItem>(
            item =>
            {
                Trace.WriteLine(
                    string.Format("{0}: {1}", Environment.CurrentManagedThreadId, item.operation));

                try
                {
                    item.d(item.state);
                }
                finally
                {
                    item.SetResult(true);
                }
            });

    public override SynchronizationContext CreateCopy()
    {
        return new TplDataflowSynchronizationContext();
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        Trace.WriteLine(
            string.Format("{0}: Posting...", Environment.CurrentManagedThreadId));

        this.ab.Post(new ActionItem { d = d, state = state, operation = "Post" });

        Trace.WriteLine(
            string.Format("{0}: Posted.", Environment.CurrentManagedThreadId));
    }

    public override void Send(SendOrPostCallback d, object state)
    {
        Trace.WriteLine(
            string.Format("{0}: Sending...", Environment.CurrentManagedThreadId));

        ActionItem item = new ActionItem { d = d, state = state, operation = "Send" };
        this.ab.SendAsync(item);
        item.Task.Wait();

        Trace.WriteLine(
            string.Format("{0}: Sent.", Environment.CurrentManagedThreadId));
    }

    public class ActionItem : TaskCompletionSource<bool>
    {
        public SendOrPostCallback d;
        public object state;
        public string operation;
    }
}

UPDATE:


Fixed implementation because the SynchronizationContext.Send method returned without having completely executed the operation. Thanks to Svick for pointing that out and Stephen Toub for the help fixing it.

C# 5.0 Async/Await Demo Code

I’ve published the sample code I use to demonstrate the use of async/await in C# 5.0. You can find it here.

Projects

PauloMorgado.AyncDemo.WebServer

This project is a simple web server implemented as a console application using Microsoft ASP.NET Web API self hosting and serves an image (with a delay) that is accessed by the other projects.

This project has a dependency on Json.NET due to the fact the the Microsoft ASP.NET Web API hosting has a dependency on Json.NET.

The application must be run on a command prompt with administrative privileges or a urlacl must be added to allow the use of the following command:

netsh http add urlacl url=http://+:9090/ user=machine\username


To remove the urlacl, just use the following command:



netsh http delete urlacl url=http://+:9090/


PauloMorgado.AsyncDemo.WindowsForms



This Windows Forms project contains three regions that must be uncommented one at a time:



Sync with WebClient



This code retrieves the image through a synchronous call using the WebClient class.



Async with WebClient



This code retrieves the image through an asynchronous call using the WebClient class.



Async deadlock



This code how async operations can still deadlock.



Async with HttpClient with cancelation



This code retrieves the image through an asynchronous call with cancelation using the HttpClient class.



PauloMorgado.AsyncDemo.Wpf



This WPF project contains three regions that must be uncommented one at a time:



Sync with WebClient



This code retrieves the image through a synchronous call using the WebClient class.



Async with WebClient



This code retrieves the image through an asynchronous call using the WebClient class.



Async deadlock



This code how async operations can still deadlock.



Async with HttpClient with cancelation



This code retrieves the image through an asynchronous call with cancelation using the HttpClient class.