假设:
λ: >let x = Control.Concurrent.MVar.newMVar ""
λ: >:t x
x :: IO (MVar [Char])
我试图致电putMVar
:
λ: >:t putMVar
putMVar :: MVar a -> a -> IO ()
λ: >:t x
x :: IO (MVar [Char])
但它失败了
λ: >x >>= \y -> putMVar y "foo"
:^?^?
*** Exception: thread blocked indefinitely in an MVar operation
为什么会失败?如何使用x
而不是"foo"
更新""
?
答案 0 :(得分:6)
让我们查看文档:
data MVar a
MVar(发音为“em-var”)是一个同步变量,用于 并发线程之间的通信。它可以被认为是一个 框,可能是空的或满的。
和
newMVar :: a -> IO (MVar a)
创建一个包含所提供值的MVar。
和
putMVar :: MVar a -> a -> IO ()
将值放入MVar。如果MVar当前已满,putMVar将会 等到它变空。
putMVar还有两个重要的属性:
putMVar是单唤醒。也就是说,如果有多个线程 在putMVar中被阻塞,并且MVar变空,只有一个线程会 被唤醒运行时保证唤醒线程完成 它的putMVar操作。当一个MVar阻塞多个线程时, 它们以FIFO顺序被唤醒。这对于提供公平性很有用 使用MVars构建的抽象属性。
melpomene的答案包含正确的解释。我让我的答案留在这里引用文献。
答案 1 :(得分:6)
x
不是MVar
。这是一个创建MVar
的操作,即newMVar ""
的另一个名称。
x >>= \y -> putMVar y "foo"
是一项创建MVar
并将其命名为y
的操作。然后,它会尝试将"foo"
放入MVar
。但是,y
已包含""
,因此putMVar
会阻止。它不会永远阻止,因为y
是此操作中的局部变量,这意味着没有其他人可以访问它并且不存在任何读者。 putMVar
检测到这种情况(死锁)并引发异常。
你应该做的是从以下开始:
x <- newMVar ""
这使x
成为MVar
。
然后你可以拿出旧值(""
):
takeMVar x
并在
中添加新值putMVar x "foo"
(MVar
不支持在一个步骤中替换现有值;首先必须将其取出,然后输入新值。)