在bool值上使用Interlocked.CompareExchange()操作?

Bha*_*udi 26 c# multithreading

我有两个问题:

  1. 是否需要使用Interlocked类来访问布尔值?默认情况下,是否读取或写入布尔值atomic?

  2. 我尝试在布尔上使用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

  1. 分别读取或写入布尔值原子的,但"比较和交换"同时读取和写入同一地址,这意味着整个事务不是原子的.如果多个线程可以写入同一位置,则需要使用Interlocked该类使整个事务成为原子.

  2. 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)

  • 这看起来有点过于复杂.如何使用单个静态实例"Truth"创建一个"AtomicBoolean"类,并将对"Truth"的任何引用解释为true,将任何其他引用解释为false? (2认同)

Gia*_*rdi 9

您可以使用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)