在C#/ .NET中,不可重置的“标志”线程是否安全?

时间:2013-05-09 13:12:56

标签: c# .net asynchronous thread-safety

(注意:我已经问过这个问题了,但答案是针对Java的,所以我问的是C#和.NET框架的相同问题。它不是重复的。)

我一直在使用这种模式,但我最近才开始认为这样做可能不太好。基本上,我使用了这种模式的一些变体:

public class SampleAsync
{
    public SampleAsync() { }

    private bool completed;
    public void Start()
    {
        var worker = new BackgroundWorker();
        worker.DoWork += (sender, e) => {
            //... do something on a different thread
            completed = true;
        };
        worker.RunWorkerAsync();
    }

    public void Update()
    {
        if (!completed) return;
        //... do something else
    }
}

*用户有责任确保Start仅被调用一次。无论何时何地都会调用Update

我一直认为这在C#/ .NET框架中是线程安全的,因为即使没有严格的同步,我也只是将completed设置为true。一旦观察到true,它就不会重置为false。它在构造函数中初始化为false,根据定义,它是线程安全的(除非你在其中做了一些愚蠢的事情)。那么,以这种方式使用不可复制的标志是否安全? (如果是这样,它甚至可以提供任何性能优势吗?)

由于

2 个答案:

答案 0 :(得分:3)

您的代码是线程安全的,因为bool是原子类型。

MSDN:

  

以下数据类型的读写是原子的:bool,char,   byte,sbyte,short,ushort,uint,int,float和reference类型。在   添加,读取和写入具有基础类型的枚举类型   以前的列表也是原子的。读写其他类型的,   包括long,ulong,double和decimal,以及用户定义的   类型,不保证是原子的。除了图书馆   为此目的而设计的功能,不保证原子   read-modify-write,例如在递增或递减的情况下。

请参阅:http://msdn.microsoft.com/en-us/library/aa691278(v=vs.71).aspx

请使用volatile

标记您的字段
 private volatile bool completed;

MSDN:

  

volatile关键字表示可以在中修改字段   通过诸如操作系统,硬件或a之类的程序来编程   同时执行线程。

请参阅:http://msdn.microsoft.com/en-us/library/x13ttww7(v=vs.71).aspx

答案 1 :(得分:3)

它严重依赖于目标架构。英特尔处理器具有强大的内存模型,因此您可能会使用这样的代码。但是那时的抖动可能会搞砸你。例如,x86抖动易于将变量存储在cpu寄存器中,尤其是当if()语句出现在紧密循环中时。只有这样才能在Release版本中实现,这是一个很棒的调试噩梦。声明变量 volatile 是一个创可贴。 x64抖动不需要,至少在当前版本中是这样。但是,创可贴倾向于不会阻止具有弱内存模型的处理器,如ARM和Itanium。他们当然不会保证变量的更新状​​态很快就会在另一个线程中可见。线程调度程序倾向于刷新cpu缓存。最终

没有正确做到这一点没有意义。使用适当的同步对象,如AutoResetEvent。或Interlocked.CompareExchange(),如果你担心周期。