使用布尔标志来阻止线程在C#中运行是否安全?

Kir*_*ril 35 c# multithreading thread-safety

我主要关心的是布尔标志......没有任何同步就可以安全地使用它吗?我在几个地方读到它是原子的(包括文档).

class MyTask
{
    private ManualResetEvent startSignal;
    private CountDownLatch latch;
    private bool running;

    MyTask(CountDownLatch latch)
    {
        running = false;
        this.latch = latch;
        startSignal = new ManualResetEvent(false);
    }

    // A method which runs in a thread
    public void Run()
    {
        startSignal.WaitOne();
        while(running)
        {
            startSignal.WaitOne();
            //... some code
        }
        latch.Signal();
    }

    public void Stop()
    {
        running = false;
        startSignal.Set();
    }

    public void Start()
    {
        running = true;
        startSignal.Set();
    }

    public void Pause()
    {
        startSignal.Reset();
    }

    public void Resume()
    {
        startSignal.Set();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是以这种方式设计任务的安全方式吗?有什么建议,改进,评论吗?

注意:我写了我的自定义CountDownLatch类,以防你想知道我从哪里得到它.

更新:
这也是我的CountDownLatch:

public class CountDownLatch 
{
    private volatile int m_remain;
    private EventWaitHandle m_event;

    public CountDownLatch (int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException();
        m_remain = count;
        m_event = new ManualResetEvent(false);
        if (m_remain == 0)
        {
            m_event.Set();
        }
    }

    public void Signal()
    {
        // The last thread to signal also sets the event.
        if (Interlocked.Decrement(ref m_remain) == 0)
            m_event.Set();
    }

    public void Wait()
    {
        m_event.WaitOne();
    }
}
Run Code Online (Sandbox Code Playgroud)

Rem*_*anu 48

你最好标记它volatile:

volatile关键字指示可能由多个并发执行的线程修改字段.声明为volatile的字段不受编译器优化的约束,这些优化假定由单个线程进行访问.这可确保始终在字段中显示最新值.

但我会改变你的循环:

    startSignal.WaitOne();
    while(running)
    {
        //... some code
        startSignal.WaitOne();
    }
Run Code Online (Sandbox Code Playgroud)

正如在你的帖子中那样,"某些代码"可能在线程停止时(即调用Stop时)执行,这是意外的,甚至可能是不正确的.

  • @Lirik:你刚才所说的非常重要,让我们重复一遍:**在最一般的情况下,原子性与波动性没有任何关系**.*C#*的规则是这样的,所有volatile都是原子的,但不是相反的; 有些东西是原子的,没有波动.CLR没有这样的保证; 在CLR中,波动率和原子性是正交的.您可以使用非原子非易失性,原子非易失性,非原子易失性和原子易失性读写. (14认同)
  • 如果您没有将其标记为volatile,则生成的代码可能会将值优化到注册表中,您的线程将*永远*看不到更改. (6认同)
  • @Remus我现在明白了:原子性与线程之间的可见性无关......只是因为在一个CPU周期中执行一个操作并不意味着结果对其他线程是可见的,除非该值是明显不稳定. (4认同)

Mic*_*urr 5

布尔值在C#中是原子的,但是,如果要在一个线程中修改它并在另一个线程中读取它,则至少需要将其标记为volatile。否则,读取线程实际上只能将其一次读取到一个寄存器中。