Is there anyway of replacing this code using the Interlocked.Exchange
API?
if (IsWorking == false)
{
lock (this)
{
if (IsWorking == false)
{
IsWorking = true;
}
}
}
Is there anyway of replacing this code using the Interlocked.Exchange
API?
if (IsWorking == false)
{
lock (this)
{
if (IsWorking == false)
{
IsWorking = true;
}
}
}
You would usually use Interlocked.CompareExchange
to do that. But unfortunatley there isn't an overload that accepts a boolean and the object
and generic overloads work only on reference types. You can however hack that and use an int
with only 2 values (0 and 1):
private static int IsWorking = 0;
private static void Main()
{
var originalValue = Interlocked.CompareExchange(ref IsWorking, 1, 0);
}
Interlocked.CompareExchange
, as the name suggest, compares 2 values (IsWorking
and 0) and if they are equal stores the other value (1) in the original location. The return value is what used to be in the original location before invoking this atomic method. So if a 0 was returned the call replaced the value in IsWorking
and if it's 1 then a different thread got there first.
About Interlocked.CompareExchange
:
"If comparand and the value in location1 are equal, then value is stored in location1. Otherwise, no operation is performed. The compare and exchange operations are performed as an atomic operation. The return value of CompareExchange is the original value in location1, whether or not the exchange takes place."
First of all, IsWorking = true
would be equivalent unless there is other concurrent code that is holding the lock when this executes. Assignments of primitve values like that are guaranteed to be atomic. Clearly the combination of the conditional and the assigment might not be atomic. However, it appears that the code wants to set IsWorking to true only if IsWorking is now false. However, if IsWorking is now true, what would be the harm in re-setting it to True? It seems like you're missing something that wants a thread-safe way to notify the outside world of a state change. This is where you might use an Event or a Monitor.
You might also be looking for Interlocked.CompareExchange
but that will only work for reference types and primitive numeric types. So you would need to change from a boolean to an int for example. However, the CompareExchange method won't wait as your lock will. It will simply return the old value regardless of whether it was replaced or not.
So if you changed IsWorking from a boolean property to an int field, you could:
int wasWorking = Interlocked.CompareExchange(ref isWorking, 1, 0);
if wasWorking is 0, you know you changed the state, if wasWorking is 1, you know you didn't change the state.
Yes, but whether it would be useful is another matter.
You could replace
isWorking
with an integer, and then useInterlocked.CompareExchange(ref isWorking, 1, 0)
.This is pointless though; either way the result at the end is that
isWorking
is1
, so we would have better concurrency by just replacing the code withIsWorking = true
(or perhaps aVolatileWrite
to ensure it was seen by other CPUs).Your code is probably a reduction of something like:
And it's the
DoSomethingWorthDoing()
part that falls down.There are ways of improving the concurrency of such double-checked locks, but it depends on a few things. One example is:
At the end of this
someUsefulThing
will be set to the result ofSomeUsefulFactory()
, and after being set it will not be set again. There will though be a period in which we might callSomeUsefulFactory()
multiple times just to throw the result away. Sometimes that's a disaster, sometimes that's fine, and sometimes we could have just done no locking and been fine; it depends on just why we're concerned to share the same object here.There are further variants, but applicability depends on just why you care about concurrency. This code, for example, implements a thread-safe concurrent dictionary using such interlocked operations, which tends to be slower than just putting a
lock
around accessing a dictionary when contention is low, but much better when many threads access it at the same time.