问题
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?
回答1:
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.
回答2:
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:
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).
来源:https://stackoverflow.com/questions/12315109/confused-on-usage-of-interlocked-increment-and-lock