Partager via


How to: Create and Terminate Threads (C# Programming Guide) 

This example demonstrates how an auxiliary or worker thread can be created and used to perform processing in parallel with the primary thread. Making one thread wait for another and gracefully terminating a thread are also demonstrated. For background information on multi-threading, see Managed Threading and Using Threading (C# Programming Guide).

The example creates a class named Worker that contains the method that the worker thread will execute called DoWork. This is essentially the Main function for the worker thread. The worker thread will begin execution by calling this method, and terminate automatically when this method returns. The DoWork method looks like this:

public void DoWork()
{
    while (!_shouldStop)
    {
        Console.WriteLine("worker thread: working...");
    }
    Console.WriteLine("worker thread: terminating gracefully.");
}

The Worker class contains an additional method that is used to indicate to DoWork that it should return. This method is called RequestStop, and looks like this:

public void RequestStop()
{
    _shouldStop = true;
}

The RequestStop method merely assigns the _shouldStop data member to true. Because this data member is checked by the DoWork method, this has the indirect effect of causing DoWork to return, thereby terminating the worker thread. However, it is important to note that DoWork and RequestStop will be executed by different threads. DoWork is executed by the worker thread, and RequestStop is executed by the primary thread, so the _shouldStop data member is declared volatile, like this:

private volatile bool _shouldStop;

The volatile keyword alerts the compiler that multiple threads will access the _shouldStop data member, and therefore it should not make any optimization assumptions about the state of this member. For more information, see volatile (C# Reference).

The use of volatile with the _shouldStop data member allows us to safely access this member from multiple threads without the use of formal thread synchronization techniques, but only because _shouldStop is a bool. This means that only single, atomic operations are necessary to modify _shouldStop. If, however, this data member were a class, struct, or array, accessing it from multiple threads would likely result in intermittent data corruption. Consider a thread that changes the values in an array. Windows regularly interrupts threads in order to allow other threads to execute, so this thread could be halted after assigning some array elements but before assigning others. This means the array now has a state that the programmer never intended, and another thread reading this array may fail as a result.

Before actually creating the worker thread, the Main function creates a Worker object and an instance of Thread. The thread object is configured to use the Worker.DoWork method as an entry point by passing a reference to this method to the Thread constructor, like this:

Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);

At this point, although the worker thread object exists and is configured, the actual worker thread has yet been created. This does not happen until Main calls the Start method:

workerThread.Start();

At this point the system initiates the execution of the worker thread, but it does so asynchronously to the primary thread. This means that the Main function continues to execute code immediately while the worker thread simultaneously undergoes initialization. To insure that the Main function does not try to terminate the worker thread before it has a chance to execute, the Main function loops until the worker thread object's IsAlive property gets set to true:

while (!workerThread.IsAlive);

Next, the primary thread is halted briefly with a call to Sleep. This insures that the worker thread's DoWork function will execute the loop inside the DoWork method for a few iterations before the Main function executes any more commands:

Thread.Sleep(1);

After the 1 millisecond elapses, Main signals to the worker thread object that it should terminate using the Worker.RequestStop method introduced previously:

workerObject.RequestStop();

It is also possible to terminate a thread from another thread with a call to Abort, but this forcefully terminates the affected thread without concern for whether it has completed its task and provides no opportunity for the cleanup of resources. The technique shown in this example is preferred.

Finally, the Main function calls the Join method on the worker thread object. This method causes the current thread to block, or wait, until the thread that the object represents terminates. Therefore Join will not return until the worker thread returns, thereby terminating itself:

workerThread.Join();

At this point only the primary thread executing Main exists. It displays one final message, and then returns, terminating the primary thread as well.

The complete example appears below.

Example

using System;
using System.Threading;

public class Worker
{
    // This method will be called when the thread is started.
    public void DoWork()
    {
        while (!_shouldStop)
        {
            Console.WriteLine("worker thread: working...");
        }
        Console.WriteLine("worker thread: terminating gracefully.");
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
    // Volatile is used as hint to the compiler that this data
    // member will be accessed by multiple threads.
    private volatile bool _shouldStop;
}

public class WorkerThreadExample
{
    static void Main()
    {
        // Create the thread object. This does not start the thread.
        Worker workerObject = new Worker();
        Thread workerThread = new Thread(workerObject.DoWork);

        // Start the worker thread.
        workerThread.Start();
        Console.WriteLine("main thread: Starting worker thread...");

        // Loop until worker thread activates.
        while (!workerThread.IsAlive);

        // Put the main thread to sleep for 1 millisecond to
        // allow the worker thread to do some work:
        Thread.Sleep(1);

        // Request that the worker thread stop itself:
        workerObject.RequestStop();

        // Use the Join method to block the current thread 
        // until the object's thread terminates.
        workerThread.Join();
        Console.WriteLine("main thread: Worker thread has terminated.");
    }
}

Sample Output

main thread: starting worker thread...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: terminating gracefully...
main thread: worker thread has terminated

See Also

Tasks

Threading Sample

Reference

Threading (C# Programming Guide)
Using Threading (C# Programming Guide)
volatile (C# Reference)
Thread
Mutex
Monitor
Start
IsAlive
Sleep
Join
Abort

Concepts

C# Programming Guide

Other Resources

Managed Threading
Threading Samples