- LONG WINAPI InterlockedExchangeAdd(PLONG Addend, LONG Value)
- {
- __asm {
- mov ecx, Addend
- mov eax, Value
- xadd [ecx], eax
- }
- }
问题是,其他架构的CPU没有提供类似的指令可以锁定总线,Interlocked API的原子性如何保证?拿ARM架构来说,在Windows CE上InterlockedExchangeAdd的ARM实现如下:
- LEAF_ENTRY InterlockedExchangeAdd
- ldr r12, [r0]
- add r2, r12, r1
- str r2, [r0]
- mov r0, r12 ; (r0) = return original value
- bx lr
- ENTRY_END InterlockedExchangeAdd
- LONG InterlockedExchangeAdd(PLONG Addend, LONG Value)
- {
- LONG oldval = *Target; // ldr r12, [r0]
- LONG newval = oldval+Value; // add r2, r12, r1
- *Target = newval; // str r2, [r0]
- return oldval; // mov r0, r12; bx lr
- }
- LONG g_lVar = 0;
- void thread_entry()
- {
- LONG oldval = InterlockedExchangeAdd(&g_lVar, 1);
- }
Windows CE怎么解决这一问题?答案是调度器!在CPU被切换到另外一个context执行前(比如当前任务被中断打断,或者发生一个Data Abort)调度器会检查原先任务是否运行到Interlocked API所在的地址范围,如果是的话,调度器把指令寄存器修正为该Interlocked API的入口地址。在我们的例子中,InterlockedExchangeAdd执行完第一句时间片用完,调度器发现该线程的指令寄存器处于 InterlockedExchangeAdd API之间,因此它会把指令寄存器重置回InterlockedExchangeAdd的入口地址,再切换到线程2执行。在线程2完成后线程1运行 时,oldval的值重新从*Target(即g_lVar,此时已被线程2改为1)取得,这样,在线程1完成后g_lVar的值变成2。要注意的是,调 度器不保证Interlocked API的执行不被打断,它保证的是Interlocked API的一次完整执行不会被其他任务打断!还要注意的是,在多处理器或多核系统上,这种方法是行不通的,必须在硬件层次上提过某种锁定总线的内存访问机制 才行。