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