Passing Parameters to Threads

Often when you start a thread, you want to give it some parameters - usually some data it has to process, a queue to wait on, etc. The ThreadStart delegate doesn't take any parameters, so the information has to be stored somewhere else if you are going to create a new thread yourself. Typically, this means creating a new instance of a class, and using that instance to store the information. Often the class itself can contain the delegate used for starting the thread. 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
{
    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 the thread pool instead

One alternative to starting the thread using a ThreadStart delegate is to use the thread pool, either using ThreadPool.QueueUserWorkItem or by calling a delegate asynchronously. Both of these are covered later on in a more detailed discussion of the thread pool, which also contains examples. Note that calling a delegate asynchronously allows you to specify multiple parameters, and those parameters are strongly typed.

.NET 2.0 solution 1: anonymous methods (with and without the thread pool)

One of the enhancements to C# in version 2.0 is 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 (and using inference of delegate type too):

ThreadStart starter = delegate { 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 = delegate (object state) { Fetch ((string)state); };
ThreadPool.QueueUserWorkItem (callback, myUrl);

Note the way that the state is declared.

.NET 2.0 solution 2: ParameterizedThreadStart

In .NET 2.0, there is a new delegate, ParameterizedThreadStart, which takes a parameter of type object. You can create a thread using an instance of this delegate instead of just ThreadStart, and a new overload to Thread.Start allows you to specify the value to be passed to the new thread. This is simple, but only accepts a single parameter and isn't type-safe (just like the options when using thread pool threads). The earlier code could then be rewritten as:

[In some method or other]
Thread t = new Thread (new ParameterizedThreadStart(FetchUrl));
t.Start (myUrl);

[And the actual method...]
static void FetchUrl(object url)
{
    // use url here, probably casting it to a known type before use
}

Next page: Data Races and Locking
Previous page: Introduction and basics


Back to the main C# page.