我有两个问题:
是否需要使用Interlocked类来访问布尔值?默认情况下,是不是读取或写入布尔值原子?
我尝试在布尔值上使用Interlocked.CompareExchange并收到以下错误:
bool value = true;
Interlocked.CompareExchange<bool>(ref value, false, true);
错误:类型'bool'必须是引用类型才能在泛型类型或方法'System.Threading.Interlocked.CompareExchange(ref T,T,T)中使用它作为参数'T'
我如何解决这个问题?
答案 0 :(得分:23)
分别读取或写入布尔值是原子,但“比较和交换”同时读取和写入同一地址,这意味着整个事务不强>原子。如果多个线程可以写入同一位置,则需要使用Interlocked
类使整个事务处于原子状态。
public static T CompareExchange<T>(ref T a, T b, T c)) where T : class
重载只能与引用类型一起使用(注意最后的where T : class
子句)。您可以使用CompareExchange(Int32, Int32, Int32)
重载而不是布尔值,并使用Int32
切换布尔值。
或者,如果要保留boolean类型的变量,可以使用lock
方法来确保线程安全。这将是一个略微更慢的解决方案,但根据您的性能要求,这可能仍然是首选方式。
答案 1 :(得分:11)
滚动你自己的“AtomicBoolean”类(包装Interlocked.CompareExchange(...)
)
using System.Threading;
public class AtomicBoolean
{
private const int TRUE_VALUE = 1;
private const int FALSE_VALUE = 0;
private int zeroOrOne = FALSE_VALUE;
public AtomicBoolean()
: this(false)
{ }
public AtomicBoolean(bool initialValue)
{
this.Value = initialValue;
}
/// <summary>
/// Provides (non-thread-safe) access to the backing value
/// </summary>
public bool Value
{
get
{
return zeroOrOne == TRUE_VALUE;
}
set
{
zeroOrOne = (value ? TRUE_VALUE : FALSE_VALUE);
}
}
/// <summary>
/// Attempt changing the backing value from true to false.
/// </summary>
/// <returns>Whether the value was (atomically) changed from false to true.</returns>
public bool FalseToTrue()
{
return SetWhen(true, false);
}
/// <summary>
/// Attempt changing the backing value from false to true.
/// </summary>
/// <returns>Whether the value was (atomically) changed from true to false.</returns>
public bool TrueToFalse()
{
return SetWhen(false, true);
}
/// <summary>
/// Attempt changing from "whenValue" to "setToValue".
/// Fails if this.Value is not "whenValue".
/// </summary>
/// <param name="setToValue"></param>
/// <param name="whenValue"></param>
/// <returns></returns>
public bool SetWhen(bool setToValue, bool whenValue)
{
int comparand = whenValue ? TRUE_VALUE : FALSE_VALUE;
int result = Interlocked.CompareExchange(ref zeroOrOne, (setToValue ? TRUE_VALUE : FALSE_VALUE), comparand);
bool originalValue = result == TRUE_VALUE;
return originalValue == whenValue;
}
}
使用示例:
class MultithreadedClass
{
private AtomicBoolean isUpdating = new AtomicBoolean(false);
public void Update()
{
if (!this.isUpdating.FalseToTrue())
{
return; //a different thread is already updating
}
try
{
//... do update.
}
finally
{
this.isUpdating.Value = false; //we are done updating
}
}
}
测试用例(如果您打算在生产中使用它):
[TestClass]
public class AtomicBooleanTest
{
[TestMethod]
public void TestAtomicBoolean()
{
AtomicBoolean b = new AtomicBoolean();
Assert.IsFalse(b.Value);
b = new AtomicBoolean(false);
Assert.IsFalse(b.Value);
b = new AtomicBoolean(true);
Assert.IsTrue(b.Value);
//when Value is already true, FalseToTrue fails
b.Value = true;
Assert.IsFalse(b.FalseToTrue());
Assert.IsTrue(b.Value);
//when Value is already false, TrueToFalse fails
b.Value = false;
Assert.IsFalse(b.TrueToFalse());
Assert.IsFalse(b.Value);
//Value not changed if SetWhen fails
b.Value = false;
Assert.IsFalse(b.SetWhen(true, true));
Assert.IsFalse(b.Value);
//Value not changed if SetWhen fails
b.Value = true;
Assert.IsFalse(b.SetWhen(false, false));
Assert.IsTrue(b.Value);
}
}
答案 2 :(得分:8)
你不能使用interlocked for Boolean。你最好使用int代替。
答案 3 :(得分:8)
您可以int
对int boolValue = 0;
// ...
if (System.Threading.Interlocked.Exchange(ref boolValue, 1) == 1)
{
// Was True
}
else
{
// Was False
}
使用Interlocked.Exchange:
{{1}}