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

时间:2010-03-19 04:14:56

标签: 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();
    }
}

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

注意:我写了我的自定义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();
    }
}

4 个答案:

答案 0 :(得分:48)

你最好将它标记为volatile

  

volatile关键字表示a   字段可能会被多个修改   并发执行线程。字段   声明为volatile的不是   受编译器优化的影响   假设由单个线程访问。这个   确保最新的价值   在任何时候都在现场。

但我会改变你的循环:

    startSignal.WaitOne();
    while(running)
    {
        //... some code
        startSignal.WaitOne();
    }

正如你的帖子中那样,“某些代码”可能会在线程停止时执行(即调用Stop时),这是意外的,甚至可能是错误的。

答案 1 :(得分:5)

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

答案 2 :(得分:2)

答案 3 :(得分:0)

顺便说一句,我刚才注意到这部分代码:

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

您需要使用“startSignal.Set()”取消阻塞工作线程两次,以便执行while块中的代码。

这是故意的吗?