Export (0) Print
Expand All

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

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

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 about multi-threading, see Managed Threading and Using Threading (C# Programming Guide).

The example creates a class named Worker that contains the method, DoWork, which the worker thread will execute. 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 named RequestStop, and looks like this:


public void RequestStop()
{
    _shouldStop = true;
}


The RequestStop method just 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, which terminates the worker thread. However, you should notice 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 that 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 enables you 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 cause 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. Therefore, this thread could be stopped after assigning some array elements but before assigning others. Because the array now has a state that the programmer never intended, another thread that reads this array may fail.

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 not yet been created. This does not occur 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 ensure 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 IsAlive property of the worker thread object is set to true:


        while (!workerThread.IsAlive);

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


        Thread.Sleep(1);

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


        workerObject.RequestStop();

It is also possible to terminate a thread from another thread by using a call to Abort. This forcefully terminates the affected thread even if it has not 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 also.

Here is the complete 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.");
    }
}


Here is the 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

Show:
© 2014 Microsoft