Anu*_*ain 8 concurrency haskell
我正在尝试理解GHC最新文档中的MVar示例-
data SkipChan a = SkipChan (MVar (a, [MVar ()])) (MVar ())
newSkipChan :: IO (SkipChan a)
newSkipChan = do
sem <- newEmptyMVar
main <- newMVar (undefined, [sem])
return (SkipChan main sem)
putSkipChan :: SkipChan a -> a -> IO ()
putSkipChan (SkipChan main _) v = do
(_, sems) <- takeMVar main
putMVar main (v, [])
mapM_ (sem -> putMVar sem ()) sems
getSkipChan :: SkipChan a -> IO a
getSkipChan (SkipChan main sem) = do
takeMVar sem
(v, sems) <- takeMVar main
putMVar main (v, sem:sems)
return v
dupSkipChan :: SkipChan a -> IO (SkipChan a)
dupSkipChan (SkipChan main _) = do
sem <- newEmptyMVar
(v, sems) <- takeMVar main
putMVar main (v, sem:sems)
return (SkipChan main sem)
Run Code Online (Sandbox Code Playgroud)
我理解大部分计划,但有两个问题 -
putSkipChan原子一样的操作吗?它似乎putMVar通过先做一个避免阻塞takeMVar.但是,那不是失败,如果别的要求putMVar后,takeMVar但之前putMVar?在这种情况下,程序似乎会永远阻止.dupSkipChan附加sem到信号量列表SkipChan?不是那样做的getSkipChan.在我看来,在尝试两次唤醒同一个信号量时,调用dupSkipChan后跟getSkipChan(这似乎是你需要做多个读者)会导致阻塞putSkipChan?你是对的,另一个线程可以调用putMVar main并搞砸了putSkipChan.但是创建上述代码的模块不会导出SkipChan构造函数,因此这样的恶意操作是不可能的.
dupSkipChan创建一个新的 emptyMVar调用sem并将其添加到main中的列表中.它不会添加在其中创建的预先存在的一个newSkipChan.因此没有障碍.
向其他读者解释这个问题和评论的更多内容:这个想法是可能有多个读者线程.最初SkipChan main sem1是唯一这样的读者. dupSkipChan做一个SkipChan main sem2.如果有成千上万的读者,那么你不会想要通知他们所有的新值putSkipChan,因此设计是getSkipChan将其sem放入main中的列表中.初始化SkipChan完成,newSkipChan并且dupSkipChan还包括将新空值sem放入main中的列表中.
上述初始化和设计意味着第一次getSkipChan获得已经写入的最近的过去值(或者阻止第一个值到达).getSkipChan对此的未来SkipChan将始终获得比之前获得的更新的值,并且如果该值已经可用,这些将不会阻止.