confused on usage of Interlocked.Increment and Lock

4.1k views Asked by At

I understand the functionality of Interlocked.Increment and lock(). But I'm confused on when to use one or the other. As far as I can tell Interlocked.Increment increments shared int/long value, whereas as lock() is meant to lock region of code.

For example, if i want to update string value it is possible with lock():

lock(_object)
{
    sharedString = "Hi";
}

However, this is not possible with Interlocked class.

  • Why can't this be done via Interlocked?
  • What's the difference between these synchronization mechanisms?
3

There are 3 answers

0
Shaun Wilson On

Interlocked.Increment and related methods rely on hardware instructions to perform synchronized modification of a single 32bit or 64bit memory value, ensuring that multiple threads accessing the same value do not read/write stale data. this is necessary because at a hardware level a processor has a local/bus copy of memory values (for performance, often referred to as bus memory or cpu cache).

lock(){} performs synchronization for a section of code, rather than a single integral value. and instead of relying on hardware instructions to synchronize access to a variable the resulting code instead relies on operating system synchronization primitives (software, not hardware) to protect memory and code execution.

Further, the use of lock() emits a memory barrier, ensuring that accessing the same variables from multiple CPUs yields synchronized (non-stale) data. This is not true in other languages/platforms, where a memory barriers and fencing must be explicitly performed.

It's more efficient to use Interlocked methods on integral values because the hardware has native support for performing the necessary synchronization. but this hardware support only exists for native integrals such as __int32 and __int64, since the hardware does not have a notion of higher level complex types no such high level method is exposed from the Interlocked type. Thus you can't use Interlocked to synchronize the assignment of System.String or any System.Object derived types.

(Even though the assignment of a pointer to a string value can be done with the same hardware instruction if you were using a lower level language, the fact is that in .NET a string object is not represented as a pointer and thus it's just not possible in any "pure" .NET language. I am avoiding the fact that you can use unsafe methods to resolve the pointer and do an interlocked assignment of string values if you -really- wanted to, but I don't feel this is really what you are asking about, and further this is not supported by Interlocked because under the hood GC pinning would need to occur, which would likely become more expensive and invasive than using lock().)

Thus, for synchronized modification/assignment of "reference types" you will need to use a synchronization primitive (i.e. lock(){}, Monitor, etc). If all you need to synchronize is a single integral value (Int32, Int64) it would be more efficient to use the Interlocked methods. It may still make sense to use lock() statement if there are multiple integral values to synchronize, for example incrementing one integer while decrementing a second integer, where both need to be synchronized as a single logical operation.

3
Andrey On

Interlocked.Increment can and should be used to increment shared int variable. Functionally using Interlocked.Increment is same as:

lock(_object)
{
   counter++;
}

but Interlocked.Increment is much cheaper performance-wise.

3
vgru On

If you want to exchange a reference value, and return the original value in an atomic operation, you can use Interlocked.Exchange. Interlocked.Increment does exactly what it says it does: it increments a number.

But simply assigning a reference value to a variable, or any 32-bit value type is atomic in .NET anyway. The only other case I can think of, in which the latter doesn't hold, is if you create a packed structure and set attributes which will force the compiler not to align members at 4-byte boundaries (but this is not something you do really often).