[英]Using Interlocked.CompareExchange() operation on a bool value?
我有兩個問題:
是否需要使用聯鎖 class 來訪問 boolean 值? 默認情況下,對 boolean 值的讀取或寫入不是原子的嗎?
我嘗試在 boolean 上使用 Interlocked.CompareExchange 並收到以下錯誤:
bool value = true; Interlocked.CompareExchange<bool>(ref value, false, true);
錯誤:類型“bool”必須是引用類型才能將其用作泛型類型或方法“System.Threading.Interlocked.CompareExchange(ref T, T, T)”中的參數“T”
我如何解決這個問題?
分別讀取或寫入 boolean 值是原子的,但“比較和交換”對同一地址進行讀取和寫入,這意味着整個事務不是原子的。 如果多個線程可以寫入同一位置,則需要使用Interlocked
class 使整個事務原子化。
public static T CompareExchange<T>(ref T a, T b, T c)) where T: class
重載只能與引用類型一起使用(注意結尾處的where T: class
)。 您可以使用CompareExchange(Int32, Int32, Int32)
重載,並將 boolean 與Int32
切換,而不是 boolean 值。
或者,如果要保留 boolean 類型的變量,可以使用lock
方法來確保線程安全。 這將是一個稍慢的解決方案,但根據您的性能要求,這可能仍然是首選方式。
滾動您自己的“AtomicBoolean” class (包裝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);
}
}
為此,您可以在int
上使用Interlocked.Exchange :
int boolValue = 0;
// ...
if (System.Threading.Interlocked.Exchange(ref boolValue, 1) == 1)
{
// Was True
}
else
{
// Was False
}
您不能對 Boolean 使用互鎖。 你最好使用 int 代替。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.