有人可以解释atomicModifyIORef
的工作原理吗?特别是:
(1)它是否等待锁定,或者如果存在争用(如TVar
)则乐观地尝试重试。
(2)为什么atomicModifyIORef
的签名与modifyIORef
的签名不同?特别是,这个额外的变量是什么b
?
编辑:我想我已经找到了(2)的答案,因为b
是一个要提取的值(如果不需要,这可以是空的)。在单线程程序中,知道该值是微不足道的,但在多线程程序中,人们可能想知道在应用函数时先前的值是什么。我假设这就是为什么modifyIORef
没有这个额外的返回值的原因(因为modifyIORef
使用此返回值的这种用法可能应该使用atomicModifyIORef
。我仍然对答案感兴趣(1)虽然。
答案 0 :(得分:29)
是否等待锁定,或乐观地尝试重试,如果存在争用(如TVar)。
atomicModifyIORef在你所使用的底层硬件架构上使用锁定指令,以原子方式将指针交换到已分配的Haskell对象。
在x86上,它使用cas intruction,通过atomicModifyMutVar#
作为语言暴露给语言,在Cmm中实现为运行时服务:
stg_atomicModifyMutVarzh
{
...
retry:
x = StgMutVar_var(mv);
StgThunk_payload(z,1) = x;
#ifdef THREADED_RTS
(h) = foreign "C" cas(mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var, x, y) [];
if (h != x) { goto retry; }
#else
StgMutVar_var(mv) = y;
#endif
...
}
也就是说,它会尝试进行交换,否则重试。
cas作为基元的实现显示了我们如何深入到金属:
/*
* Compare-and-swap. Atomically does this:
*/
EXTERN_INLINE StgWord cas(StgVolatilePtr p, StgWord o, StgWord n);
/*
* CMPXCHG - the single-word atomic compare-and-exchange instruction. Used
* in the STM implementation.
*/
EXTERN_INLINE StgWord
cas(StgVolatilePtr p, StgWord o, StgWord n)
{
#if i386_HOST_ARCH || x86_64_HOST_ARCH
__asm__ __volatile__ (
"lock\ncmpxchg %3,%1"
:"=a"(o), "=m" (*(volatile unsigned int *)p)
:"0" (o), "r" (n));
return o;
#elif arm_HOST_ARCH && defined(arm_HOST_ARCH_PRE_ARMv6)
StgWord r;
arm_atomic_spin_lock();
r = *p;
if (r == o) { *p = n; }
arm_atomic_spin_unlock();
return r;
#elif !defined(WITHSMP)
StgWord result;
result = *p;
if (result == o) {
*p = n;
}
return result;
因此,您可以看到它能够在英特尔中使用原子指令,在其他架构上将使用不同的机制。运行时将重试。
答案 1 :(得分:10)
atomicModifyIORef
需要r :: IORef a
和函数f :: a -> (a, b)
并执行以下操作:
它读取r
的值并将f
应用于此值,产生(a',b)
。然后使用新值r
更新a'
,而b
是返回值。这种读写访问是以原子方式完成的。
当然,只有通过r
对atomicModifyIORef
进行所有次访问时,此原子性才有效。
请注意,您可以通过查看源[1]来找到此信息。