arrow-left arrow-right brightness-2 chevron-left chevron-right circle-half-full dots-horizontal facebook-box facebook loader magnify menu-down RSS star Twitter twitter GitHub white-balance-sunny window-close
ThreadPool vs. Tasks
1 min read

ThreadPool vs. Tasks

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

.NET 4.0 includes a new few new classes called Tasks, which are part of the Task Parallel Library. You can learn all about them in an article by my friend Sacha on Code Project.

The TPL is useful, but I'm starting to see a lot of coders using the Task class. I may be an old fuddy-duddy, but I can't quite understand what advantage Task gives me over plain old ThreadPool in .NET 2.0. Here are some examples of how the Tasks "features" would be implemented using ThreadPool.

Starting a task

ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeWork();
});

Waiting for a task to complete

var handle = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeWork();
    handle.Set();
});
handle.WaitOne();

Returning

int result = 0;

ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeWork();

    result = 42;

    handle.Set();
});
handle.WaitOne();

Console.WriteLine(result);

Chaining tasks

var handle = new AutoResetEvent(false);
ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeWork();
    handle.Set();
});
handle.WaitOne();

ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeMoreWork();
    handle.Set();
});
handle.WaitOne();

Waiting for multiple tasks to complete

var handle1 = new ManualResetEvent(false);
var handle2 = new ManualResetEvent(false);

ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeWork();
    handle1.Set();
});

ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeMoreWork();
    handle2.Set();
});

WaitHandle.WaitAll(new WaitHandle[] { handle1, handle2 });

Exception handling

var handle = new ManualResetEvent(false);

Exception error = null;
ThreadPool.QueueUserWorkItem(delegate
{
    try
    {
        DoSomeWork();
    }
    catch (Exception ex)
    {
        error = ex;
    }
    finally
    {
        handle.Set();
    }
});

handle.WaitOne();

if (error != null)
    Console.WriteLine("Error! " + error);

Word of caution: you should probably always do this when using ThreadPool, since exceptions thrown on a ThreadPool thread will tear down the AppDomain if not caught

Cancellation

var cancel = false;

ThreadPool.QueueUserWorkItem(delegate
{
    while (!cancel)
    {
        Thread.Sleep(100);
    }
});

cancel = true;

Note: this is like a gazillion times more complex in Tasks

Dispatching to UI thread

var dispatcher = Application.Current.Dispatcher;

ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeWork();

    dispatcher.BeginInvoke(new Action(() => progressBar.Value += 10));

    DoSomeMoreWork();

    dispatcher.BeginInvoke(new Action(() => progressBar.Value += 10));
});

Being notified when a task is complete without blocking

Action<int> done = (int x) => Console.WriteLine("Done! " + x);

ThreadPool.QueueUserWorkItem(delegate
{
    DoSomeWork();

    done(42);
});

So help me learn the Task API - how would using Task make the examples above look better?

Paul Stovell's Blog

Hello, I'm Paul Stovell

I'm a Brisbane-based software developer, and founder of Octopus Deploy, a DevOps automation software company. This is my personal blog where I write about my journey with Octopus and software development.

I write new blog posts about once a month. Subscribe and I'll send you an email when I publish something new.

Subscribe

Comments