5

我正在做一些关于并发性和内存可见性的实验,并遇到了这种奇怪的行为(见内联评论):

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 -NGHC 可以看到 4 个功能。根据 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
4

2 回答 2

3

ghc仅在定义明确的安全点(仅在分配内存时)挂起线程。我相信您的分叉线程永远不会分配内存,因此它永远不会将控制权交给其他线程。因此,一旦编译器安排分叉线程(有时在您的threadDelay.

您可以在“轻量级线程和并行性”部分了解更多关于安全点的信息。

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

于 2014-09-01T02:57:41.610 回答
0

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

于 2014-09-02T03:02:45.620 回答