What is the threading class if I just want to test and set a flag in a threadsafe manner?
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));
}
}
精彩评论