Forked IORef阅读器功能似乎停止主线程

时间:2014-08-31 23:16:02

标签: haskell concurrency ghc 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个功能。 Per Gabriel Gonzalez的回答我尝试了以下内容,看看调度程序是否将两个线程放在同一个物理处理器上:

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

2 个答案:

答案 0 :(得分:3)

ghc仅在明确定义的安全点处挂起线程,这些安全点仅在分配内存时才有。我相信你的分叉线程永远不会分配内存,因此它永远不会放弃对其他线程的控制。因此,一旦编译器调度分叉线程(在threadDelay中间的某个时间),主线程就不会进展。

您可以在&#34; Lightweight Threads and Parallelism&#34;中找到有关安全点here的更多信息。

编辑:正如托马斯所提到的,当您遇到类似这样的情况时,您可以使用Control.Concurrent.yield明确放弃控制权。

答案 1 :(得分:0)

看起来这可能是一个古老的ghc bug #367