2014-08-31 45 views
5

我在做一些實驗併發和內存的知名度和跑進這種奇怪的行爲(見註釋在線):叉狀IOREF閱讀器的功能似乎停滯主線程

module Main 
    where 

import Data.IORef 
import Control.Concurrent 
import System.CPUTime 

import System.IO 

main = do 
    hSetBuffering stdout NoBuffering 

    r <- newIORef False 
    putStrLn "forking..." -- PRINTED 
    forkIO $ f r 
    threadDelay 1000000 

    putStrLn "writeIORef" -- NEVER PRINTED 
    writeIORef r True 

    threadDelay maxBound 

f :: IORef Bool -> IO() 
f r = readIORef r >>= \b-> if b then print "NEVER PRINTED" else f r 

我期待也許writeIORef不被對於子線程是可見的,但對於主線程來說並不簡單(顯然)失速。

編上GHC 7.8.3

cabal exec ghc -- --make -fforce-recomp -O2 -threaded visibility.hs 

./visibility +RTS -N 

跑這裏發生了什麼?

編輯:所以我的機器有兩個真正的核心和兩個超線程核心,所以+RTS -N GHC看到4個能力。每加布裏埃爾·岡薩雷斯的回答我嘗試了以下,看看也許調度是把兩個線程都在同一個物理處理器上:

module Main 
    where 

import Data.IORef 
import Control.Concurrent  
import GHC.Conc(threadCapability,myThreadId,forkOn) 

main = do  
    r <- newIORef False 
    putStrLn "going..." 

    (cap,_) <- threadCapability =<< myThreadId 
    forkOn (cap+1) $ f r     -- TRIED cap+1, +2, +3.... 
    threadDelay 1000000 

    putStrLn "writeIORef"     -- BUT THIS STILL NEVER RUNS 
    writeIORef r True 

    threadDelay maxBound 

f :: IORef Bool -> IO() 
f r = readIORef r >>= \b-> if b then print "A" else f r 

回答

3

ghc只掛起線程在明確安全點,當內存分配這只是。我相信你分叉的線程永遠不會分配內存,所以它永遠不會放棄對其他線程的控制。因此,一旦編譯器安排分叉線程(有時在你的threadDelay的中間),你的主線程永遠不會前進。

您可以在「輕量級線程和平行度」部分了解有關安全點here的更多信息。

編輯:正如托馬斯所說,你可以使用Control.Concurrent.yield明確放棄控制,當你遇到這樣的情況。

+2

正是。一個解決方案是使用「yield」來分割沒有分配或其他類似點的塊。因此,對於這個問題中的忙碌等待,我們會有'... else yield >> f r'。顯然,繁忙的循環首先是一個糟糕的主意。另一種方法是使用'MVar'和'takeMVar'來代替信號。 – 2014-09-01 03:07:22

+0

對不起,也許我正在密集...爲什麼我需要分叉線程來控制回主線程,以便讓'putStrLn「writeIORef」'運行?我用'+ RTS -N'運行,用'-threaded'編譯;兩個線程不應該同時運行嗎? – jberryman 2014-09-01 12:45:16

+0

請參閱我的編輯,以及 – jberryman 2014-09-01 14:47:33