在C#中,从多个线程写入bool变量是否安全?

在C#中,从多个线程写入bool变量是否安全?,c#,multithreading,task-parallel-library,C#,Multithreading,Task Parallel Library,我需要检查Parallel.ForEach的任何迭代是否达到特定点。根据我的理解,如果我的bool是一个volatile字段,则以下内容是安全的,但它必须是封闭方法中的一个变量: bool anySuccess = false; Parallel.ForEach(things, thing => { // stuff happens... anySuccess = true; }); if(!anySuccess) throw new Exception("No t

我需要检查
Parallel.ForEach
的任何迭代是否达到特定点。根据我的理解,如果我的
bool
是一个
volatile
字段,则以下内容是安全的,但它必须是封闭方法中的一个变量:

bool anySuccess = false;
Parallel.ForEach(things, thing =>
{
    // stuff happens...
    anySuccess = true;
});
if(!anySuccess)
    throw new Exception("No things succeeded :(");

我可以按原样使用此代码,还是应该使用
锁定
联锁
功能?

如果该代码是循环访问该布尔值的整体,那么对我来说是安全的

一般来说,基本值类型不是线程安全的,因为对它们执行的许多操作都不是原子的

但是如果你做的唯一一件事就是给这个变量赋值。。。从不阅读它,从不根据它更改分支,从不根据它的当前状态向它写入。。。所有的写操作都是相同的(唯一可能发生的修改是将其设置为
true
),那么我看不出非原子性会导致任何问题

==添加==

以上对现行规范正确性的评论。作为代码库的一部分,代码在其上下文中的长期安全性也是值得考虑的。让代码保持原样,是在为未来的开发人员设置一个陷阱,他们不知道/理解/认识正在发生的事情,以及为什么它目前是安全的

如果你把它放在这里,那么就必须对声明和单一用法做出明确的评论,解释发生了什么,为什么它是安全的,以及为什么它们不能开始以其他方式读取/使用变量


另一种选择(添加锁定代码)从长远来看更安全,但性能也可能稍差。

我会锁定布尔。另外,我建议将if语句移动到Parallel.Foreach块中。原因:

这将在并行循环之后进行计算,因此只读取anySuccess的最后一次更新

bool anySuccess = false;
Parallel.ForEach(things, thing =>
{
    // stuff happens...
    anySuccess = true;
});
if(!anySuccess)
    throw new Exception("No things succeeded :(");
这将评估bool的所有更新

Object myLock = new Object();

bool anySuccess = false;
Parallel.ForEach(things, thing =>
{
    // stuff happens...
    lock (myLock) 
    {
        anySuccess = true;
    }
    if(!anySuccess)
        throw new Exception("No things succeeded :(");
});

如果对
anySuccess
的唯一写入操作是将其设置为true,并且直到处理完成后才读取它,那么您可能会侥幸逃脱。但我还是建议使用锁。您也可以使用Volatile.Read at last(!anySuccess)check。在这种特定情况下,对anySuccess执行的唯一操作是将其设置为true。这意味着不同线程之间不可能存在任何竞争条件,比如一个线程将anySuccess设置为false,另一个线程同时将其设置为true。所以在这里你甚至不需要锁或并行foreach;即使在同一时刻触发所有线程,也不会发生任何不好的情况。至少需要在末尾执行
Volatile.Read
,否则编译器有权使用缓存值。即使没有,处理器的缓存也可能根本没有跟上更新。实际上,您不太可能遇到这个问题,但是对于线程代码,“不太可能”和“bug”一样好。@jeroenmoster:您能更详细地解释一下吗?对anySuccess的调用是在整个Parallel.ForEach循环完成后进行的。为什么仍然允许编译器从循环未完全执行的点获取缓存值?如果读取anySuccess的线程看不到它被更改,因为它将读取缓存在CPU寄存器中的值(false),该怎么办?你不需要Volatile吗?在那里读吗?@Evk没有任何东西在读它。。。这就是我回答的全部要点,怎么样?如果(!anySuccess)block?@Evk,我很有信心,一旦所有并行线程都解决了(即在Parallel.ForEach方法完成之前),任何变量修改都将保证完成。因此,下一行(if)保证得到正确的值。关于这个问题本身的评论似乎在争论这个问题,我不清楚他们得出了什么结论。@vrtola Parallel for each显然是一个同步点(这意味着一个完整的内存障碍),否则整个方法将毫无意义,也不可能实现。thread.Join和所有任务等价物也是如此。这是功能的变化。鉴于anySuccess对于到达此点的第一个“事物”未设置为true,因此它将立即终止,即使尚未处理所有事物。使用原始代码,可以确保正确处理所有内容。