c# - 在 C# 中访问简单的 bool 标志时,是否需要锁定或标记为 volatile ?

标签 c# .net multithreading locking thread-safety

假设您有一个在后台线程上运行的简单操作。您想要提供一种方法来取消此操作,因此您创建了一个 bool 标志,您从取消按钮的点击事件处理程序中将其设置为 true。

private bool _cancelled;

private void CancelButton_Click(Object sender ClickEventArgs e)
{
    _cancelled = true;
}

现在您正在从 GUI 线程设置取消标志,但您正在从后台线程读取它。在访问 bool 之前是否需要锁定?

您是否需要这样做(并且显然也锁定了按钮单击事件处理程序):

while(operationNotComplete)
{
    // Do complex operation

    lock(_lockObject)
    {
        if(_cancelled)
        {
            break;
        }
    }
}

或者这样做是否可以接受(没有锁):

while(!_cancelled & operationNotComplete)
{
    // Do complex operation
}

或者如何将 _cancelled 变量标记为易变的。有必要吗?

[我知道有 BackgroundWorker 类及其内置的 CancelAsync() 方法,但我感兴趣的是这里锁定和线程变量访问的语义和使用,而不是具体实现,代码只是一个示例。]

似乎有两种说法。

1) 因为它是一个简单的内置类型(并且在 .net 中对内置类型的访问是原子的)并且因为我们只在一个地方写入它并且只在后台线程上读取所以不需要锁定或标记作为 volatile 。
2)你应该将它标记为易变的,因为如果你不这样做,编译器可能会优化 while 循环中的读取,因为它认为它没有能力修改值。

哪种方法是正确的? (为什么?)

[编辑:在这方面似乎有两种明确定义和对立的思想流派。我正在寻找关于这个问题的明确答案,所以如果可能的话,请发表你的理由并在你的答案中引用你的来源。]

最佳答案

首先,线程是棘手的;-p

是的,尽管所有谣言都相反,但访问时需要要么使用 lock volatile(但不能同时使用两者)来自多个线程的 bool

对于简单的类型和访问,例如退出标志 (bool),那么 volatile 就足够了 - 这确保线程不会将值缓存在它们的寄存器中(这意味着:其中一个线程永远不会看到更新)。

对于较大的值(其中原子性是一个问题),或者您想要同步操作的序列(典型示例是“如果不存在并添加”字典访问),lock 是更通用。这充当内存屏障,因此仍然为您提供线程安全性,但提供其他功能,例如脉冲/等待。请注意,您不应在值类型或 lock 上使用 string;也不是 Typethis ;最好的选择是将您自己的锁定对象作为一个字段 (readonly object syncLock = new object();) 并锁定它。

举个例子,说明如果不同步它会严重中断(即永远循环)- see here

要跨越多个程序,像 Mutex*ResetEvent 这样的 OS 原语也可能有用,但这对于单个 exe 来说太过分了。

关于c# - 在 C# 中访问简单的 bool 标志时,是否需要锁定或标记为 volatile ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1222184/

相关文章:

.net - DispatcherTimer 与 WPF 应用程序中用于任务调度程序的常规 Timer

c# - 为什么异常总是被接受为返回类型(抛出时)?

java - Java 中的线程安全双向关联

c# - 单例实现在 32 位但不是 64 位上工作正常

c# - 线程优先级(如何获得固定顺序)

c# - Microsoft.TeamTest.targets 中的 MSBuild NullReferenceException

c# - 如何在 XAML 中设置 DataGrid 左上角的样式?

c# - string.ToLower 和 TextInfo.ToLower 之间的区别

java - 单机如何共享线程池?

c# - 简单注入(inject)器:如何跳过对容器中对象的验证