Please note: this page is now obsolete. It is now part of my larger article about threading.

Starting threads with parameters in C#

In the C# newsgroup, quite a few people have asked how parameters should be passed to new threads. In other words, if a new thread needs specific information in order to run (as it often does), how do you get that information to the thread? The ThreadStart delegate doesn't take any parameters, so the information has to be stored somewhere else. Typically, this means creating a new instance of a class, and using that instance to store the information. For example, we might have a program which needs to fetch the contents of various URLs, and wants to do it in the background. You could write code like this:

public class UrlFetcher
{
    private string url

    public UrlFetcher (string url)
    {
        this.url = url;
    }

    public void Fetch()
    {
        // use url here
    }
}

[... in a different class ...]
              
UrlFetcher fetcher = new UrlFetcher (myUrl);
new Thread (new ThreadStart (fetcher.Fetch)).Start();

In some cases, you actually just wish to call a method in some class (possibly the currently executing class) with a specific parameter. In that case, you may wish to use a nested class whose purpose is just to make this call - the state is stored in the class, and the delegate used to start the thread just calls the "real" method with the appropriate parameter. (Note that the object on which to call the method in the first place will also be required as state unless the method is static.)

Using ThreadPool instead

One choice you should always be aware of when starting a new thread is whether it would actually make more sense to use ThreadPool, which will recycle threads for you, and queue items if the pool is full. ThreadPool is a good choice for jobs which are fairly short and frequent, as it avoids wasting time creating and destroying threads.

To add a job to the pool, you merely need to call ThreadPool.QueueUserWorkItem with a WaitCallback delegate. One incidental advantage of using a WaitCallback is that you can also specify the "state" to use when you queue the work item, and this is presented to the delegate as a method parameter. It's not strongly typed however (the passed type is just object) and I would suggest having a more strongly typed method which is called by the WaitCallback delegate and which should be used in preference elsewhere in code. For instance, you might define two methods to fetch URLs as:

// You'd use this method elsewhere in code, if you needed it
public void Fetch (string url)
{
    this.url = url;
}

// You'd use this as the WaitCallback delegate to queue in the thread pool
private void Fetch(object url)
{
    Fetch ((string) url);
}

// And you'd call it like this:
ThreadPool.QueueUserWorkItem (new WaitCallback (Fetch), myUrl);

Calling a delegate asynchronously

Another alternative (which also uses the thread pool internally) is to call a delegate asynchronously. This has the advantage of allowing you to specify more than one parameter, and have those parameters checked in terms of type at compile-time. This relies on the C# compiler adding a couple of methods to the declared delegate type: BeginInvoke and EndInvoke. The BeginInvoke method always takes whatever parameters the delegate itself does, along with an AsyncCallback parameter and a plain object parameter as state. It returns an IAsyncResult reference. When BeginInvoke is called, a work item is placed on the thread pool work queue, and when it executes the delegate is called with the specified parameters. When the delegate has completed, the AsyncCallback parameter delegate is called with the resulting IAsyncResult reference which can be used to find out the original state parameter, the delegate itself, the return value of the operation (via the delegate), and more. Note that to find out the original delegate you need to cast the IAsyncResult reference to AsyncResult. I'm not aware of why the framework is designed in this way, but if you don't like to make that cast you can always provide the delegate itself as the state parameter. The EndInvoke method only takes an IAsyncResult, and has the same return type as the delegate itself, returning the result of the delegate's execution. Here's a complete example:

using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

class AsyncTest
{
    delegate string UrlFetcher(string url);
    
    public static void Main()
    {
        UrlFetcher u = new UrlFetcher (Fetch);
        u.BeginInvoke ("some url", 
                       new AsyncCallback (AfterFetch),
                       "this is state");
        // Just to demonstrate stuff going on while
        // the fetch happens...
        for (int i=0; i < 10; i++)
        {
            Console.WriteLine ("Foreground thread counter: {0}", i);
            Thread.Sleep(1000);
        }
    }
    
    static string Fetch (string url)
    {
        // Just to simulate it taking a while to fetch the
        // contents
        Thread.Sleep (5000);
        return "Contents of the url";
    }
    
    static void AfterFetch (IAsyncResult result)
    {
        Console.WriteLine ("Delegate completed.");
        Console.WriteLine ("  State: {0}", result.AsyncState);
        AsyncResult async = (AsyncResult) result;
        UrlFetcher fetcher = (UrlFetcher) async.AsyncDelegate;
        Console.WriteLine ("  Contents: {0}", fetcher.EndInvoke(result));
    }
} 

The output of the above is (in one test run - it might vary very slightly because the counter may get a bit further before the delegate's Sleep call returns):

Foreground thread counter: 0
Foreground thread counter: 1
Foreground thread counter: 2
Foreground thread counter: 3
Foreground thread counter: 4
Delegate completed.
  State: this is state
  Contents: Contents of the url
Foreground thread counter: 5
Foreground thread counter: 6
Foreground thread counter: 7
Foreground thread counter: 8
Foreground thread counter: 9	

See the MSDN documentation for the BeginRead and EndRead operations in the System.IO.Stream class for more examples of how this kind of asynchronous operation works.

Important note: You should always call EndInvoke on delegates which are executed asynchronously, even if you don't actually care about the result of the operation - otherwise you may provoke a memory leak. Fortunately there are ways to make this easier, as shown in the Advanced-DotNet mailing list archives.

A future solution: anonymous methods

One of the future enhancements to C# is almost certain to be anonymous methods. These allow you to specify blocks of code as methods within other methods, and use those methods as delegates. You can access variables (including local variables and parameters of the "outside" method) within the anonymous method. For example, using an anonymous method to fetch a URL using a normal ThreadStart delegate:

ThreadStart starter = new ThreadStart()
{
    Fetch (myUrl);
};
new Thread(starter).Start();

(This could have all been expressed within a single step, creating both the thread and the delegate in the same line of code, but I believe the above is more readable.) Here's similar code to use a WaitCallback and queue the job in ThreadPool:

WaitCallback callback = new WaitCallback (state)
{
    Fetch ((string)state);
};
ThreadPool.QueueUserWorkItem (callback, myUrl);

Note the way that the state is declared. This is, of course, subject to change. For more details about anonymous methods and other potential enhancements to C#, see the Microsoft C# Language Future Features page.


Back to the main page.