开发者

What is the threading class if I just want to test and set a flag in a threadsafe manner?

开发者 https://www.devze.com 2022-12-16 18:10 出处:网络
I just want to do a simple, though thread-safe, boolean test (and set) so: if(myBoolean==false)//should not lock/wait!

I just want to do a simple, though thread-safe, boolean test (and set) so:

if(myBoolean==false)   //should not lock/wait!
{ 
     myBoolean=true;
     .....
}
else
{
     ....
}

I considered the following (although possibly incorrectly, so please correct me where I misunderstood)

  • using the Lock { if(myBoolean)... } construct seems like a overkill way to do it. And, it also locks the thread while it waits for the lock to become free. I don't want this.
  • 开发者_开发技巧
  • The AutoResetEvent class does have a concept of a boolean state, but it is used to signal another thread which is waiting. So not relevant in my case
  • Semaphore class has a notion of a reference count (probably to throttle the amount of access to a resource?). So probably not what I'm after.
  • Mutex class. As far as I understood, this is the same principal as the Lock primitive

Anyone have an idea what is the class/construct to do this in an efficient manner?


Consider Interlocked.CompareExchange.


The answer (Interlocked.CompareExchange) was already given, but here's my usage example:

private int _isDisposing;

public bool IsDisposing
{
    get
    {
        return this._isDisposing != 0;
    }
}

public void Dispose()
{
    // Side note: I may want to `return` instead of `throw`
    if (Interlocked.CompareExchange(ref _isDisposing, 1, 0) != 0)
        throw new InvalidOperationException("Dispose was recursively called.");

    try
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    finally
    {
        _isDisposing = 0;
    }
}


You're probably looking for the Interlocked class, specifically for Interlocked.CompareExchange.


To have a thread safe test and set operation the code have to block other threads. To avoid locking uneccesary, you can use the test-and-test-and-set pattern:

if (something) {
   lock(_sync) {
      if (something) {
         something = false;
         ...
      }
   }
}

The second test is needed to be sure that some other thread didn't change the value between the first test and the lock.


For collections, "Test and Add" I use

    /// <summary>
    /// If h contains v then return true, else add v to h and return false.
    /// Thread safe on h.
    /// </summary>
    /// <param name="h"></param>
    /// <param name="v"></param>
    /// <returns></returns>
    bool TestAndAdd(HashSet<string> h, string v)
    {
        lock(h)
        {
            if(h.Contains(v))
            {
                return true;
            }
            h.Add(v);
            return false;
        }
    }

Then I can test and set like:

if (!TestAndAdd(usedCodes, mc.code))


I had the same issue many times, and don't think that I could keep in mind that the last argument of Interlocked.CompareAndChange() is the comparand. Here's what I came up with.

using System.Threading;

public class AtomicFlag
{
    public const int SETVALUE = 1;
    public const int RESETVALUE = 0;

    /// <summary>
    /// Represents the current state of the flag.
    /// 0 means false (or reset).
    /// 1 means true (or set).
    /// </summary>
    private int Value;

    /// <summary>
    /// Creates an atomicflag with the specified default value.
    /// </summary>
    /// <param name="initialValue">AtomicFlag.SETVALUE or 
    /// AtomicFlag.RESETVALUE. Defaults to RESETVALUE.</param>
    public AtomicFlag(int initialValue = RESETVALUE)
    {
        Guard.AgainstUnsupportedValues<int>(initialValue, "initialValue",
           new int[] { SETVALUE, RESETVALUE });
        Value = initialValue;
    }


    public void Set()
    {
        Value = SETVALUE;
    }

    public void Reset()
    {
        Value = RESETVALUE;
    }


    public bool TestAndSet()
    {
        // Use Interlocked to test if the current value is RESETVALUE,
        // return true and set value to SETVALUE.
        //
        // From Interlocked.CompareExchange help:
        // public static int CompareExchange(
        //    ref int location1,
        //    int value,
        //    int comparand
        // )
        // where
        //  location1: The destination, whose value is compared with 
        //             comparand and possibly replaced.
        //  value:     The value that replaces the destination value if the
        //             comparison results in equality.
        //  comparand: The value that is compared to the value at
        //             location1. 
        return (RESETVALUE == Interlocked.CompareExchange(
            ref Value, SETVALUE, RESETVALUE));
    }

    public bool TestAndReset()
    {
        // If the current value is SETVALUE, return true and change value 
        // to RESETVALUE.
        return (SETVALUE ==
            Interlocked.CompareExchange(ref Value, RESETVALUE, SETVALUE));
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号