[Solved] Using Interlocked.CompareExchange() operation on a bool value?

I have two questions:

  1. Is there a need to use Interlocked class for accessing boolean values? Isn’t a read or a write to a boolean value atomic by default?

  2. I tried using Interlocked.CompareExchange on a boolean and got the following error:

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

    Error: The type ‘bool’ must be a reference type in order to use it as parameter ‘T’ in the generic type or method ‘System.Threading.Interlocked.CompareExchange(ref T, T, T)’

How do I go about solving this problem?

Solution #1:

  1. Reading or writing boolean values separately is atomic, but “compare and exchange” does both reading and writing to the same address, which means that entire transaction is not atomic. If multiple threads can write to this same location, you need to make the entire transaction atomic, by using the Interlocked class.

  2. public static T CompareExchange<T>(ref T a, T b, T c)) where T : class overload can only be used with reference types (note the where T : class clause at the end). Instead of a boolean value, you can use the CompareExchange(Int32, Int32, Int32) overload, and switch the boolean with an Int32.

    Alternatively, if you want to keep your variables of boolean type, you can use the lock method to ensure thread safety. This would be a slightly slower solution, but depending on your performance requirements, this might be still the preferred way.

Respondent: Groo

Solution #2:

Roll your own “AtomicBoolean” class (that wraps 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;
    }
}

Example Usage:

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
        }
    }
}

Test cases (if you’re going to use it in production):

[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);
    }
}
Respondent: EthanB

Solution #3:

You can use Interlocked.Exchange on an int for this:

int boolValue = 0;

// ...

if (System.Threading.Interlocked.Exchange(ref boolValue, 1) == 1)
{
    // Was True
}
else
{
    // Was False                
}
Respondent: Gian Marco

Solution #4:

You can’t use interlocked for Boolean. You’re better off using an int instead.

http://connect.microsoft.com/VisualStudio/feedback/details/98293/interlocked-compareexchange-should-offer-an-overload-for-boolean

Respondent: Shiroy

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Leave a Reply

Your email address will not be published.