C# 为什么这个布尔值不是线程安全的?
我已经为一个集成测试编写了下面的代码,该测试为几个不同的程序集调用了一个昂贵的函数DoSomething()。我本以为应用锁就足以确保线程安全,但有时布尔值是真的,而在我当前的测试用例中它应该总是假的 我还尝试了Interlocked.CompareExchange的解决方案,它似乎对我也不起作用 有人能给我指出我到底做错了什么吗C# 为什么这个布尔值不是线程安全的?,c#,multithreading,thread-safety,C#,Multithreading,Thread Safety,我已经为一个集成测试编写了下面的代码,该测试为几个不同的程序集调用了一个昂贵的函数DoSomething()。我本以为应用锁就足以确保线程安全,但有时布尔值是真的,而在我当前的测试用例中它应该总是假的 我还尝试了Interlocked.CompareExchange的解决方案,它似乎对我也不起作用 有人能给我指出我到底做错了什么吗 public class SomeClass { private object _lock = new object(); private volat
public class SomeClass
{
private object _lock = new object();
private volatile bool _isSuccessful = true;
private bool IsSuccesful
{
get
{
lock (_lock)
{
return _isSuccessful;
}
}
set
{
lock (_lock)
{
_isSuccessful = value;
}
}
}
public bool Get()
{
Parallel.ForEach(..., ... =>
{
IsSuccesful = IsSuccesful & DoSomething();
});
return IsSuccesful;
}
}
你说DoSomething()
很贵(所以我猜很费时)
现在想象两个并行调用,一个是DoSometing()
成功调用(A),另一个是失败调用(B)。这显然是可能发生的情况:
issusccessful
是否为true
,因此调用DoSomething
issusccessful
,它仍然是true
,因此调用DoSomething
DoSomething
为B返回false
(好吧,它恰好快了一点),因此B将issessful
设置为false
DoSomething
返回A的true
。现在A有true和true
(因为issusccessful
读取时是true
),因此再次将issusccessful
设置为true
Parallel.ForEach
内的单个int
上
public bool Get()
{
int failed = 0;
Parallel.ForEach(..., ... =>
{
if (!DoSomething())
Interlocked.Increment(ref failed);
});
return failed == 0;
}
如果您只对其中一项测试失败感兴趣,您当然可以简单地执行以下操作:
public bool Get()
{
Parallel.ForEach(..., ... =>
{
if (!DoSomething()) IsSuccessful = false;
});
return IsSuccessful;
}
你说DoSomething()
很贵(所以我猜很费时)
现在想象两个并行调用,一个是DoSometing()
成功调用(A),另一个是失败调用(B)。这显然是可能发生的情况:
issusccessful
是否为true
,因此调用DoSomething
issusccessful
,它仍然是true
,因此调用DoSomething
DoSomething
为B返回false
(好吧,它恰好快了一点),因此B将issessful
设置为false
DoSomething
返回A的true
。现在A有true和true
(因为issusccessful
读取时是true
),因此再次将issusccessful
设置为true
Parallel.ForEach
内的单个int
上
public bool Get()
{
int failed = 0;
Parallel.ForEach(..., ... =>
{
if (!DoSomething())
Interlocked.Increment(ref failed);
});
return failed == 0;
}
如果您只对其中一项测试失败感兴趣,您当然可以简单地执行以下操作:
public bool Get()
{
Parallel.ForEach(..., ... =>
{
if (!DoSomething()) IsSuccessful = false;
});
return IsSuccessful;
}
这应该足够了。从文档:
volatile修饰符通常用于用户访问的字段
不使用lock语句序列化访问的多线程
它有效地序列化了访问
尽管如此,我认为这不是问题所在
你说测试应该总是返回false,但有时返回true。为了“知道”它总是返回false,那么所有DoSomething()调用都必须返回false。这是真的吗?如果这是一个标准(串行)foreach,那么它将始终以相同的顺序执行,因此,一旦对DoSomething的调用返回false,isSuccessful将在迭代的其余部分保持false。当你运行这个并行程序时,命令就不存在了。最终得到的执行顺序每次都可能不同。这是由于DoSomething调用可能有不同的完成时间,并且每次都可以以不同的顺序运行。您的结果可能不可重复且不一致
我想你真正想要的是一个Issuccessful,它只有在所有DoSomething返回true时才是真的。一种方法是删除&&并使用简单的if语句。例如:
public bool Get()
{
var good = true;
Parallel.ForEach(..., ... =>
{
var result = DoSomething();
if (result == false)
{
good = false;
}
});
IsSuccesful = good;
return IsSuccesful;
}
一旦“好”变为假,就没有方法将其恢复为真。因此,如果一个测试返回false,那么IsSuccessful将返回false。这也可能会让未来的开发人员更清楚地了解代码的意图。这就足够了。从文档:
volatile修饰符通常用于用户访问的字段
不使用lock语句序列化访问的多线程
它有效地序列化了访问
尽管如此,我认为这不是问题所在
你说测试应该总是返回false,但有时返回true。为了“知道”它总是返回false,那么所有DoSomething()调用都必须返回false。这是真的吗?如果这是一个标准(串行)foreach,那么它将始终以相同的顺序执行,因此,一旦对DoSomething的调用返回false,isSuccessful将在迭代的其余部分保持false。当你运行这个并行程序时,命令就不存在了。最终得到的执行顺序每次都可能不同。这是由于DoSomething调用可能有不同的完成时间,并且每次都可以以不同的顺序运行。您的结果可能不可重复且不一致
我想你真正想要的是一个Issuccessful,它只有在所有DoSomething返回true时才是真的。一种方法是删除&&并使用简单的if语句。例如:
public bool Get()
{
var good = true;
Parallel.ForEach(..., ... =>
{
var result = DoSomething();
if (result == false)
{
good = false;
}
});
IsSuccesful = good;
return IsSuccesful;
}
一旦“好”变为假,就没有方法将其恢复为真。因此,如果一个测试返回false,那么IsSuccessful将返回false。这也可能会让未来的开发人员更清楚地了解代码的意图。您能将其转化为v吗