Synchronizing .NET 4 Tasks with the UI Thread

While attending the Patterns of Parallel Programming Workshop at PDC, I picked up a very interesting tidbit from Stephen Toub’s talk: the new Task pattern in .NET 4 has built-in support for synchronization with the UI thread.  This makes writing parallel applications that interact with the user interface easier than ever before.

The key to this is the TaskScheduler.FromCurrentSynchronizationContext() method.  This method creates a scheduler from the current SynchronizationContext, automatically choosing the appropriate context.  This scheduler can then be used by nearly all of the TaskFactory methods to generate a task.

As an example, here is a very simple Form.Load event handler for a demo Windows Forms application which creates a task which will execute in the background when the form loads.  The task runs some (fake) work, and occasionally uses tasks, as well as a continuation task, to update the user interface.

private void Form1_Load(object sender, EventArgs e)
{
    // This requires a label titled "label1" on the form...
    // Get the UI thread's context
    var context = TaskScheduler.FromCurrentSynchronizationContext();

    this.label1.Text = "Starting task...";

    // Start a task - this runs on the background thread...
    Task task = Task.Factory.StartNew( () =>
        {
            // Do some fake work...
            double j = 100;
            Random rand = new Random();
            for (int i = 0; i < 10000000; ++i)
            {
                j *= rand.NextDouble();
            }

            // It's possible to start a task directly on
            // the UI thread, but not common...
            var token = Task.Factory.CancellationToken;
            Task.Factory.StartNew(() =>
            {
                this.label1.Text = "Task past first work section...";
            }, token, TaskCreationOptions.None, context);

            // Do a bit more work
            Thread.Sleep(1000);
        })
        // More commonly, we'll continue a task with a new task on
        // the UI thread, since this lets us update when our
        // "work" completes.
        .ContinueWith(_ => this.label1.Text = "Task Complete!", context);
}

The beauty of this may not be apparent at first glance, but look closely at the threading methods.  They work with the SynchronizationContext instance, which was generated by the TaskFactory for us.  Nowhere do we explicitly say to use Control.Invoke or any other Windows Forms construct.  In fact, this code will work exactly the same way, unchanged, on WPF.  The library handles assigning us the correct SynchronizationContext for us.  In Windows Forms, it will generate a context which passes everything to the message loop.  In WPF, it will automatically generated a context which uses the Dispatcher to push messages back into the WPF message queue.

About Reed
Reed Copsey, Jr. - http://www.reedcopsey.com - http://twitter.com/ReedCopsey

Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!

Powered by WP Hashcash