Skip to main content

A particularly common scenario (with a typical question being "How can I shut down a worker thread?" Here's a code skeleton which just needs the work for the worker thread to perform to be filled in (and any member it needs, of course).

using System;
using System.Threading;

/// <summary>
/// Skeleton for a worker thread. Another thread would typically set up
/// an instance with some work to do, and invoke the Run method (eg with
/// new Thread(new ThreadStart(job.Run)).Start())
/// </summary>
public class Worker
{
    /// <summary>
    /// Lock covering _stopping and stopped
    /// </summary>
    private readonly object _stopLock = new object();

    /// <summary>
    /// Whether or not the worker thread has been asked to stop.
    /// </summary>
    private bool _stopping = false;

     /// <summary>
    /// Whether or not the worker thread has _stopped.
    /// </summary>
    private bool _stopped = false;

    /// <summary>
    /// Returns whether the worker thread has been asked to stop.
    /// This continues to return true even after the thread has stopped.
    /// </summary>
    public bool Stopping
    {
        get
        {
            lock (_stopLock)
            {
                return _stopping;
            }
        }
    }

    /// <summary>
    /// Returns whether the worker thread has stopped.
    /// </summary>
    public bool Stopped
    {
        get
        {
            lock (_stopLock)
            {
                return _stopped;
            }
        }
    }

    /// <summary>
    /// Tells the worker thread to stop, typically after completing its
    /// current work item. (The thread is *not* guaranteed to have stopped
    /// by the time this method returns.)
    /// </summary>
    public void Stop()
    {
        lock (_stopLock)
        {
            _stopping = true;
        }
    }

    /// <summary>
    /// Called by the worker thread to indicate when it has stopped.
    /// </summary>
    void SetStopped()
    {
        lock (_stopLock)
        {
            _stopped = true;
        }
    }

    /// <summary>
    /// Main work loop of the class.
    /// </summary>
    public void Run()
    {
        try
        {
            while (!Stopping)
            {
                // Insert work here. Make sure it doesn't tight loop!
                // (If work is arriving periodically, use a queue and Monitor.Wait,
                // changing the Stop method to pulse the monitor as well as setting
                // _stopping.)

                // Note that you may also wish to break out *within* the loop
                // if work items can take a very long time but have points at which
                // it makes sense to check whether or not you've been asked to stop.
                // Do this with just:
                // if (Stopping)
                // {
                //     return;
                // }
                // The finally block will make sure that the _stopped flag is set.
            }
        }
        finally
        {
            SetStopped();
        }
    }
}