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

时间:2011-07-12 07:53:55

标签: c#

我有两个问题:

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

  2. 我尝试在布尔值上使用Interlocked.CompareExchange并收到以下错误:

    bool value = true;
    Interlocked.CompareExchange<bool>(ref value, false, true);
    

    错误:类型'bool'必须是引用类型才能在泛型类型或方法'System.Threading.Interlocked.CompareExchange(ref T,T,T)中使用它作为参数'T'

  3. 我如何解决这个问题?

4 个答案:

答案 0 :(得分:23)

  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)重载而不是布尔值,并使用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)

答案 3 :(得分:8)

您可以intint boolValue = 0; // ... if (System.Threading.Interlocked.Exchange(ref boolValue, 1) == 1) { // Was True } else { // Was False } 使用Interlocked.Exchange

{{1}}