在haskell中实现渠道——解决尴尬的小队

6 concurrency haskell squad

解决尴尬的小队一文中,西蒙·佩顿·琼斯 (Simon Peyton Jones) 提供了Channel.

type Channel a = (MVar (Stream a) , -- Read end
                  MVar (Stream a) ) -- Write end (the hole)

type Stream a = MVar (Item a)

data Item a = MkItem a (Stream a)
Run Code Online (Sandbox Code Playgroud)

现在,他实现了putChan :: Channel a -> a -> IO ()这样的功能

putChan (read, write) val
  = do { new_hole <- newEmptyVar ;
         old_hole <- takeMVar write ;
         putMVar write new_hole ;
         putMVar old_hole (MkItem val new_hole) }
Run Code Online (Sandbox Code Playgroud)

上面的函数从写入中取出一个 MVar,然后将一个空的 MVar 放入其中。
然后它写入从 write 中提取的 old_hole。

问题是,为什么要写入old_hole?已经从write中取出,作用域仅限于当前块,那有什么区别呢?

jbe*_*man 4

问题是,为什么要写入old_hole?它已经从 write 中取出了,并且它的作用域仅限于当前块,那有什么区别呢?

不完全的。old_hole读取端“在范围内” 。你必须看看newChan完整的图片:

newChan = do { 
    read <- newEmptyMVar ;
    write <- newEmptyMVar ;
    hole <- newEmptyMVar ;
    putMVar read hole ;
    putMVar write hole ;
    return (read,write) }
Run Code Online (Sandbox Code Playgroud)

因此,在调用newChanold_hole”之后,与中的putChan相同。随着通道操作的进展,它总是嵌套在的 s 中的某个地方。MVarholenewChanold_holeMVarread

我发现链表式通道的设计一开始确实很难理解。该论文中的插图很好地展示了结构,但基本思想是读者“剥离”一层 MVar 以显示一个值,而作者则在 MVar 堆的“底部”插入值,以维护指向最底层的指针。

顺便说一句,这是Control.Concurrent.Chan中使用的设计