2011-08-31 116 views
8
無功例如

我想了解在GHC latest docs的無功例子 -幫助理解在Haskell

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) 

我瞭解大部分程序但兩個問題 -

  1. 仿若putSkipChan操作原子?它似乎通過首先執行takeMVar來避免阻止putMVar。但是如果在takeMVar之後但在putMVar之前撥打putMVar,那麼這不會失敗嗎?在這種情況下,程序似乎會永遠封鎖。
  2. 爲什麼dupSkipChansem附加到SkipChan中的信號量列表?這不是由getSkipChan完成的。在我看來,調用dupSkipChan然後getSkipChan(這似乎是你要做多讀者)會導致一個塊時putSkipChan試圖喚醒同一信號量兩次?

回答

5
  1. 你是正確的,另一個線程可以調用putMVar main擾亂了putSkipChan。但是創建上述代碼的模塊不會導出SkipChan構造函數,所以這種流氓操作是不可能的。

  2. dupSkipChan使得emptyMVar稱爲sem並添加到主列表中。它不會添加在newSkipChan中創建的預先存在的一個。因此沒有障礙。

向其他讀者解釋這個問題和評論:這個想法是可能有多個讀者線程。最初SkipChan main sem1是唯一這樣的讀者。 dupSkipChan使得SkipChan main sem2。如果有成千上萬的讀者,那麼你不想在putSkipChan中通知他們所有的新值,因此設計是getSkipChan將其sem放入主列表中。在newSkipChandupSkipChan中初始化SkipChan還包括將新的空sem放入主列表中。

上述初始化和設計意味着第一個getSkipChan獲取最近寫入的過去值(或阻止第一個值到達)。未來getSkipChan在那SkipChan將總是得到一個比任何之前得到的新值,並且這些不會阻止,如果該值已經可用。