【发布时间】:2009-08-03 12:50:05
【问题描述】:
假设您有一个在后台线程上运行的简单操作。您想提供一种取消此操作的方法,因此您可以创建一个布尔标志,将其从取消按钮的单击事件处理程序中设置为 true。
private bool _cancelled;
private void CancelButton_Click(Object sender ClickEventArgs e)
{
_cancelled = true;
}
现在您正在从 GUI 线程设置取消标志,但您正在从后台线程读取它。访问布尔之前需要加锁吗?
您是否需要这样做(显然也要锁定按钮单击事件处理程序):
while(operationNotComplete)
{
// Do complex operation
lock(_lockObject)
{
if(_cancelled)
{
break;
}
}
}
或者是否可以这样做(没有锁):
while(!_cancelled & operationNotComplete)
{
// Do complex operation
}
或者如何将 _cancelled 变量标记为 volatile。有必要吗?
[我知道有一个BackgroundWorker类,它内置了CancelAsync()方法,但我对这里的锁定和线程变量访问的语义和使用感兴趣,而不是具体的实现,代码只是一个例子。]
似乎有两种理论。
1) 因为它是一个简单的内置类型(并且对内置类型的访问在 .net 中是原子的)并且因为我们只在一个地方写入它并且只在后台线程上读取,所以不需要锁定或标记易挥发。
2)您应该将其标记为 volatile,因为如果您不这样做,编译器可能会优化 while 循环中的读取,因为它认为它无法修改该值。
哪种方法是正确的? (为什么?)
[编辑:在这方面似乎有两种明确定义和对立的思想流派。我正在寻找一个明确的答案,所以如果可能的话,请发表你的理由,并在你的答案中引用你的来源。]
【问题讨论】:
-
是的,您确实需要
volatile或lock(作为内存屏障):请参阅stackoverflow.com/questions/458173/… -
这是否意味着西蒙将在他与 EFraim (stackoverflow.com/questions/1221839/…) 进行的大规模辩论中面红耳赤
-
@Marc:很好的例子,谢谢。 @ThePower:当我不确定时,我很高兴举起手来,所以希望我能逃脱只是略带粉红色的脸 =:)
-
Joe Duffy 的“Windows 上的并发编程”正在添加到我的书单中!
-
@Mitch:这是一本很棒的书,内容很多,尽管他偶尔会有点啰嗦。 :)
标签: c# .net multithreading locking thread-safety