5

(注意:我已经问过这个问题,但答案是针对 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,根据定义,它是线程安全的(除非你在其中做了一些愚蠢的事情)。那么,以这种方式使用不可重置标志是线程安全的吗?(如果是这样,它甚至会提供任何性能优势吗?)

谢谢

4

2 回答 2

4

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

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

于 2013-05-09T14:10:01.940 回答
3

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

MSDN:

以下数据类型的读写是原子的:bool、char、byte、sbyte、short、ushort、uint、int、float 和引用类型。此外,上一个列表中具有基础类型的枚举类型的读取和写入也是原子的。其他类型的读取和写入,包括 long、ulong、double 和 decimal,以及用户定义的类型,不保证是原子的。除了为此目的设计的库函数之外,不能保证原子读-修改-写,例如在递增或递减的情况下。

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

请用以下标记您的字段volatile

 private volatile bool completed;

MSDN:

volatile 关键字表示一个字段可以在程序中被操作系统、硬件或并发执行的线程等东西修改。

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

于 2013-05-09T13:36:27.077 回答