如果从未读取值,让多个线程写入同一个 bool 是否安全?

Is it safe to have multiple threads writing to the same bool if the value is never read?

我想出了一个有趣的情况。我有一个 bool 变量,然后我希望多个线程执行自己的独立任务,然后根据线程的结果标记该 bool。此变量 never 由任何线程读取,并且在所有写入测试完成之前 never 使用。像这样:

public bool F(){
    bool flag = false;
    Parallel.ForEach(list, element => 
    {
        bool result = // Do independent work here;
        if (result) flag = true;
    });
    return flag;
}

请注意,我从来没有读过 Parallel.ForEach 中的标志。但是可能发生的情况是有多个线程试图将 true 写入标志(但绝不会为 false)。这样做安全吗?

当您 return 时,您正在阅读 flag。因为没有锁定,所以不能保证调用 F() 的线程将读取最新值。 flag 的更新值有可能(虽然不太可能)位于处理器缓存中,如果没有某种内存屏障,它不会被刷新。

编辑:在对@dasblinkenlight 的回答的评论中,他说“.NET 保证所有副作用,包括写入变量,都在方法returns 时完成。”在那种情况下,这个答案是错误的。基于他的代表和我对线程的持续不确定性,我倾向于相信他,但我在这里问到的 some questions 的答案让我对内存可见性感到非常紧张。所以我不知道。不要过于肯定这个答案。

是的,这样做绝对安全。唯一可能发生的事情是多个线程同时将 true 写入 flag,因此您不知道哪个线程最终会覆盖什么结果,但最终结果将是相同的。

当您读取标志时,所有的写入都已完成,并且除了 true 之外,您再也不会尝试将任何内容写入 flag。因此,您只能看到以下两种情况:

  • None 的线程将任何内容写入 flag - 在这种情况下 flag 将保持 false,或者
  • 一个或多个线程将 true 写入 flag - 在这种情况下,标志将设置为 true.

如果您想完全避免这种情况,并可能节省一些执行时间,您可以这样做:

return list.AsParallel().Any(element => {
    bool result = // Do independent work here
    ...
    return result;
});

此代码不会产生等效的执行路径,因为如果其中一个线程 returns true,执行可能会提前停止。如果这是不可取的,保持你的 ForEach 方法也很好。

从根本上说,您的策略没有任何问题。同时写入 flag 严格来说不会成为问题,因为您使用它的语义有限。这里真正的问题是方法末尾的 flag 的最终读取是否安全。在这种特殊情况下,它可能是,但只是偶然。 Parallel.For 很可能在您不知情的情况下注入内存屏障,以防止线程缓存 false 值或以其他方式读取陈旧值。但是,我确实建议您养成在读取变量时通过显式调用 Volatile.Read(或任何其他内存屏障生成机制)来表明您的意图的习惯。