正常关闭工作线程

时间:2009-12-18 12:10:41

标签: c# .net concurrency multithreading

我不明白为什么在这个实现中stopped不是volatile - 如果另一个线程更新,它会被正确反映出来吗?

其次是测试(!Stopping)原子?

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>
    readonly object stopLock = new object();
    /// <summary>
    /// Whether or not the worker thread has been asked to stop
    /// </summary>
    bool stopping = false;
     /// <summary>
    /// Whether or not the worker thread has stopped
    /// </summary>
    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();
        }
    }
}

修改

此代码来自an article written by Jon Skeet

3 个答案:

答案 0 :(得分:6)

因为只能在lock内访问它。 lock 可以确保您看到最新的值。

重新原子性(我假设你真的意味着在这里同步?);它没有任何可能性;即使Stopping已同步,我们也无法再在退出lock后立即相信该值是最新的。因此,!StoppingStopping的同步性不会更高或更低。重要的是我们知道我们最近至少检查过了。有一个边缘情况,我们检查之后更改了标志,但这很好:当我们检查时,我们应该继续这样做。

答案 1 :(得分:2)

此代码的行为在C#语言规范的第3.10节中定义:

执行C#程序,以便在关键执行点保留每个执行线程的副作用。副作用定义为易失性字段的读取或写入,对非易失性变量的写入,对外部资源的写入以及抛出异常。必须保留这些副作用的顺序的关键执行点是对volatile字段(第10.5.3节),锁定语句(第8.12节)以及线程创建和终止的引用。

换句话说,lock语句足以保证避免声明停止的字段volatile。

如果lock语句仅调用Monitor.Enter()和Exit()方法,那么JIT编译器如何实现此规则是一个有趣的问题。我不认为它对这些方法有特殊的了解,我认为这是输入在Enter()调用后启动的try块的副作用。但这只是猜测。

答案 2 :(得分:1)

this entry on SO。它解释了为什么锁优先于易失性。