如果永远不会读取值,多个线程写入同一个bool是否安全?

时间:2015-01-25 02:10:02

标签: c# multithreading concurrency thread-safety

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

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写入标志(但绝不会错误)。这样做是否安全?

3 个答案:

答案 0 :(得分:4)

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

当您阅读该标志时,所有写作都已完成,您永远不会尝试将除true之外的任何内容写入flag。因此,您可以看到的唯一两种情况如下:

  • 所有帖子都没有在flag 中写入任何内容 - 在这种情况下,flag会保留false
  • 一个或多个帖子将true写入flag - 在这种情况下,该标志将设置为true

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

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

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

答案 1 :(得分:2)

当你退回时,你正在阅读flag。因为没有锁定,所以无法保证调用F()的线程将读取最新值。有可能(尽管不太可能)flag的更新值位于处理器缓存中,如果没有某种内存屏障则不会刷新。

编辑:在对@dasblinkenlight的回答的评论中,他说“.NET保证所有副作用,包括写入变量,在方法返回时完成。”在这种情况下,这个答案是错误的。基于他的代表和我对线程的持续不确定性,我倾向于相信他,但我在这里问some questions的答案让我非常担心记忆能见度。所以我不知道。不要把这个答案更加确定,而不是应得的。

答案 2 :(得分:0)

从根本上说,你的战略没有任何问题。对flag的同时写入不会成为问题,因为您使用它的语义有限。这里真正的问题是方法结束时flag的最终读数是否安全。在这种特殊情况下,它可能是,但只是偶然。 Parallel.For可能会在您不知情的情况下注入内存屏障,从而阻止线程缓存false值或以其他方式读取过时值。但是,我建议您在阅读变量时明确地调用Volatile.Read(或任何其他内存屏障生成机制)来养成您的意图。