![](/img/trans.png)
[英]Are haskell channels `Control.Concurrent.Chan` safe for multiple readers/producers?
[英]theoretical deadlock in Control.Concurrent.Chan readChan
瀏覽readChan的源代碼,從基數版本4.6開始,找到以下實現和注釋:
-- |Read the next value from the 'Chan'.
readChan :: Chan a -> IO a
readChan (Chan readVar _) = do
modifyMVarMasked readVar $ \read_end -> do -- Note [modifyMVarMasked]
(ChItem val new_read_end) <- readMVar read_end
-- Use readMVar here, not takeMVar,
-- else dupChan doesn't work
return (new_read_end, val)
-- Note [modifyMVarMasked]
-- This prevents a theoretical deadlock if an asynchronous exception
-- happens during the readMVar while the MVar is empty. In that case
-- the read_end MVar will be left empty, and subsequent readers will
-- deadlock. Using modifyMVarMasked prevents this. The deadlock can
-- be reproduced, but only by expanding readMVar and inserting an
-- artificial yield between its takeMVar and putMVar operations.
在基礎版本4.6之前,使用了modifyMVar而不是modifyMVarMasked。
我不明白這里解決了什么理論問題。 如果線程在包含readMVar的takeMVar和putMVar之間產生,則最后一句表示存在問題。 但是當readMVar在mask_下執行時,異步異常如何在成功獲取之后阻止put?
任何幫助理解這個問題的人都表示贊賞。
讓我們比較modifyMVar
和modifyMVarMasked
的來源,因為代碼從使用一個更改為使用另一個:
modifyMVar m io =
mask $ \restore -> do
a <- takeMVar m
(a',b) <- restore (io a) `onException` putMVar m a
putMVar m a'
return b
modifyMVarMasked m io =
mask_ $ do
a <- takeMVar m
(a',b) <- io a `onException` putMVar m a
putMVar m a'
return b
這里的關鍵是modifyMVar
在執行第二個參數之前調用restore
,而modifyMVarMasked
則不然。 因此,在您的問題中聲明的舊版本代碼中, readMVar
未在mask_
下mask_
! 相反,它在restore
時調用,因此可以啟用異步異常。
這是我的工作。
所以在readMVar中......
readMVar :: MVar a -> IO a
readMVar m =
mask_ $ do
a <- takeMVar m
putMVar m a
return a
...盡管使用了mask_
,運行時可能會在阻塞的takeMVar
引發異常。 請注意,在該功能中,不需要實際處理該情況; readMVar
工作,在這種情況下我們readMVar
異步異常,或者takeMVar
永遠不會成功; 無論哪種方式,我們都不會通過將其留空來打破MVar
。 ( 這是正確的嗎?這是我從我自己的相關問題的答案中拿走的。 )
modifyMVar
和modifyMVarMasked
是:
modifyMVar :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVar m io =
mask $ \restore -> do
a <- takeMVar m
(a',b) <- restore (io a) `onException` putMVar m a
putMVar m a'
return b
modifyMVarMasked :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVarMasked m io =
mask_ $ do
a <- takeMVar m
(a',b) <- io a `onException` putMVar m a
putMVar m a'
return b
...其中差異在modifyMVar
,在io a
恢復了屏蔽狀態(即異步異常可能變為未屏蔽),在我們的例子中,或多或少是readMVar
。
編輯 :雖然readMVar
也是mask_
-ed,所以現在我不明白為什么要么選擇modifyMVarMasked
或者修改modifyMVar
都會產生影響......
注釋似乎暗示yield
(插入readMVar
)是可中斷的(我無法在任何地方找到此文檔),因此可能會引發異步異常,在這種情況下將恢復readVar
(在當前版本和4.6之前的版本中) ,但在非空隊列中,讀者會看到一個空的隊列。
您可能有興趣閱讀此提交中的GHC trac,其中包含一個示例程序,當Control.Concurrent.Chan
和測試程序都編譯時,它會一致地重現此錯誤-O0
https://ghc.haskell.org/trac/ghc/ticket/6153
與此相類似:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.