Bha*_*udi 26 c# multithreading
我有两个问题:
是否需要使用Interlocked类来访问布尔值?默认情况下,是否读取或写入布尔值atomic?
我尝试在布尔上使用Interlocked.CompareExchange并得到以下错误:
bool value = true;
Interlocked.CompareExchange<bool>(ref value, false, true);
Run Code Online (Sandbox Code Playgroud)
错误:类型'bool'必须是引用类型才能在泛型类型或方法'System.Threading.Interlocked.CompareExchange(ref T,T,T)'中将其用作参数'T'
我该如何解决这个问题?
Gro*_*roo 25
分别读取或写入布尔值是原子的,但"比较和交换"同时读取和写入同一地址,这意味着整个事务不是原子的.如果多个线程可以写入同一位置,则需要使用Interlocked
该类使整个事务成为原子.
public static T CompareExchange<T>(ref T a, T b, T c)) where T : class
重载只能与引用类型一起使用(注意最后的where T : class
子句).您可以使用CompareExchange(Int32, Int32, Int32)
重载而不是布尔值,并使用a 切换布尔值Int32
.
或者,如果要保留布尔类型的变量,可以使用该lock
方法确保线程安全.这将是一个稍微慢的解决方案,但根据您的性能需求,这可能仍是首选的方式.
Eth*_*anB 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;
}
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
测试用例(如果你打算在生产中使用它):
[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);
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用Interlocked.Exchange上的int
这个:
int boolValue = 0;
// ...
if (System.Threading.Interlocked.Exchange(ref boolValue, 1) == 1)
{
// Was True
}
else
{
// Was False
}
Run Code Online (Sandbox Code Playgroud)