Is there an equivalent of the XADD command in .Net? This is, after all, the most efficient method of locking / checking开发者_运维知识库 locks for critical sections or for ensuring accurate increments in a multi-threaded environment.
I looked through the IL opcodes, but couldn't find an equivalent.
There's a lot more to acquiring a lock than a simple CPU instruction. The cost of trying to acquire it and not getting it is very high. Documented to be somewhere between 2000 and 10,000 machine instructions. The higher number for thread context switches to a thread in another process, that requires reloading the virtual memory page translation tables.
A very common strategy that makes sense on multi-core CPUs is a spin-wait. The code goes into a tight loop, burning CPU cycles heavily, trying to acquire the lock. The exact amount of time it spends in this loop is technically a tunable item, but doesn't make much difference in practice.
Anyhoo, it is the job of the CLR and the compiler is to hide the implementation details. One core class that does this is the Monitor class. It is used when you use the lock statement in C# for example, the compiler automatically translates that to a Monitor.Enter call, auto-generating a try and finally block, the finally block executes the Leave() method.
The implementation of these methods is in the CLR. There's a fairly good chunk of code there, another thing it does is dealing with a "fairness". Which makes sure that threads cannot starve, trying to acquire the lock. That code is written in C++, still pretty far removed from raw CPU instructions. It ultimately boils down to the actual code that implements the actual lock. Yes, that's written in assembly, at least in the shared source version of the CLR. That code lives in the PAL (Platform Adapter Layer), the x86 version of it looks like this:
FASTCALL_FUNC CompareExchangeMP,12
_ASSERT_ALIGNED_4_X86 ecx
mov eax, [esp+4] ; Comparand
lock cmpxchg [ecx], edx
retn 4 ; result in EAX
FASTCALL_ENDFUNC CompareExchangeMP
The cmpxchng CPU instruction with the lock prefix is the typical one to implement locks. Your xadd is covered too though, used for Interlocked.Add:
FASTCALL_FUNC ExchangeAddUP,8
_ASSERT_ALIGNED_4_X86 ecx
xadd [ecx], edx ; Add Value to Target
mov eax, edx
retn
FASTCALL_ENDFUNC ExchangeAddUP
But that isn't typically used for locks. If you want to see this for yourself, download the SSCLI20 source code and have a look-see at clr\src\wm\i386\asmhelpers.asm. Whether that is actually used in the currently shipping version of the CLR is an open question. It's pretty core, so somewhat likely. The Monitor method implementations are in clr\vm\syncblk.cpp, AwareLock class. I'm pretty sure that the SSCLI20 version of it is not what's running on your machine, they've been tinkering with the "fairness" algorithm.
The closest equivelent in .NET would be to use the Interlocked class. For example, you can use Interlocked.Add to have safe, accurate increments in a multi-threaded environment.
Take a look at the Interlocked class paying particular attention to Interlocked.Add
. The following is an example of its usage in IL.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 a,
[1] int32 b)
L_0000: ldc.i4.5
L_0001: stloc.0
L_0002: ldc.i4.7
L_0003: stloc.1
L_0004: ldloca.s a
L_0006: ldloc.1
L_0007: call int32 [mscorlib]System.Threading.Interlocked::Add(int32&, int32)
L_000c: pop
L_000d: ret
}
精彩评论