Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 哈斯克尔:你是如何做到的;原子修改ioref';工作_Haskell_Concurrency_Locking_Atomicity_Compare And Swap - Fatal编程技术网

Haskell 哈斯克尔:你是如何做到的;原子修改ioref';工作

Haskell 哈斯克尔:你是如何做到的;原子修改ioref';工作,haskell,concurrency,locking,atomicity,compare-and-swap,Haskell,Concurrency,Locking,Atomicity,Compare And Swap,有人能解释一下它是如何工作的吗?特别是: (1) 它是等待锁定,还是乐观地在出现争用时重试(如TVar)。 (2) 为什么签名与签名不同?特别是,这个额外变量是什么 编辑:我想我已经找到了(2)的答案,b是一个要提取的值(如果不需要,这个值可以为空)。在单线程程序中,知道该值很简单,但在多线程程序中,可能需要知道应用函数时先前的值是什么。我假设这就是为什么modifyIORef没有这个额外的返回值的原因(因为使用带有这个返回值的modifyIORef可能应该使用atomicModifyIORef

有人能解释一下它是如何工作的吗?特别是:

(1) 它是等待锁定,还是乐观地在出现争用时重试(如
TVar
)。
(2) 为什么签名与签名不同?特别是,这个额外变量是什么


编辑:我想我已经找到了(2)的答案,
b
是一个要提取的值(如果不需要,这个值可以为空)。在单线程程序中,知道该值很简单,但在多线程程序中,可能需要知道应用函数时先前的值是什么。我假设这就是为什么
modifyIORef
没有这个额外的返回值的原因(因为使用带有这个返回值的
modifyIORef
可能应该使用
atomicModifyIORef
。我仍然对(1)的答案感兴趣尽管如此。

原子修改IORef
采用
r::IORef a
和函数
f::a->(a,b)
,并执行以下操作:

它读取
r
的值,并将
f
应用于该值,产生
(a',b)
。然后
r
用新值
a'
更新,而
b
是返回值。这种读写访问是自动完成的

当然,只有通过
atomicModifyIORef
完成对
r
的所有访问时,这种原子性才起作用。 请注意,您可以通过查看源[1]来找到此信息

[1]

它是等待一个锁,还是乐观地在有争用的情况下反复尝试(比如TVar)

atomicModifyIORef在您所在的底层硬件体系结构上使用锁定指令,以原子方式交换指向已分配Haskell对象的指针

在x86上,它使用cas指令,通过
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;

因此,您可以看到,它能够在Intel中使用原子指令,在其他体系结构上,将使用不同的机制。运行时将重试。

它执行锁定还是乐观?GHC版本似乎只是调用GHC原语。请注意,由于惰性,
atomicModifyIORef
只需更改当前值指向一个thunk,实际工作被延迟到稍后的某个时间读取。好吧,在大多数平台上,它编译成类似CAS的东西。乐观地说,通过联锁交换(CAS)循环。@Clinton:线程将以某种顺序创建表示更新的thunk。接下来会发生什么取决于这些thunk的外观,但如果新值取决于旧值,则无论哪个线程先出现,都将对其求值并用结果覆盖thunk。如果两个线程同时尝试求值,则结果取决于在被称为“黑洞”的东西上,正在被评估的thunk可能被标记为“黑洞”。如果另一个线程尝试评估黑洞,它将阻塞,直到第一个线程完成评估。[续]也有可能两个线程同时计算thunk。因为这是纯代码,所以这是安全的,而且thunk往往很小,所以通常也没什么大不了的,但是仍然可以使用编译器标志来调整它以启用“急切的黑洞”这意味着所有被评估的thunk都被标记为黑洞,而通常只有当原始线程由于某种原因阻塞时,或者在执行垃圾收集时,才会发生这种情况。