Forked IORef reader function seems to stall main thread

最后都变了- 提交于 2019-12-23 12:24:09

问题


I was doing some experiments with concurrency and memory visibility and ran into this strange behavior (see comments inline):

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

I was expecting perhaps the writeIORef not to be visible to the child thread, but not for the main thread to simply (apparently) stall.

Compiled on ghc 7.8.3

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

and run with

./visibility +RTS -N

What's happening here?

EDIT: So my machine has two real cores and two hyperthreading cores, so with +RTS -N GHC sees 4 capabilities. Per Gabriel Gonzalez's answer I tried out the following to see if maybe the scheduler was putting both threads on the same physical processor:

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

回答1:


ghc only suspends threads at well-defined safe points, which are only when memory is allocated. I believe your forked thread never allocates memory, so it never relinquishes control to other threads. Therefore, your main thread never progresses once the compiler schedules the forked thread (sometime in the middle of your threadDelay).

You can learn more about safe points here in the section on "Lightweight Threads and Parallelism".

Edit: As Thomas mentioned, you can use Control.Concurrent.yield to explicitly relinquish control when you encounter situations like these.




回答2:


It looks like this is probably an ancient ghc bug #367.



来源:https://stackoverflow.com/questions/25597124/forked-ioref-reader-function-seems-to-stall-main-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!