【问题标题】:Forked IORef reader function seems to stall main thread分叉的 IORef 读取器函数似乎停止了主线程
【发布时间】:2014-10-25 04:02:20
【问题描述】:

我在做一些关于并发性和内存可见性的实验时遇到了这种奇怪的行为(参见 cmets 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

我期待writeIORef 可能对子线程不可见,但主线程不会简单地(显然)停止。

在 ghc 7.8.3 上编译

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

并运行

./visibility +RTS -N

这里发生了什么?

编辑:所以我的机器有两个真正的核心和两个超线程核心,所以+RTS -N GHC 可以看到 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

【问题讨论】:

    标签: haskell concurrency ghc ioref


    【解决方案1】:

    ghc 仅在定义明确的安全点暂停线程,仅在分配内存时。我相信你的分叉线程永远不会分配内存,所以它永远不会放弃对其他线程的控制。因此,一旦编译器安排分叉线程(有时在您的 threadDelay 中间),您的主线程就永远不会继续。

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

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

    【讨论】:

    • 没错。一种解决方案是使用yield 来分解没有分配或其他类似点的块。因此,对于问题中的忙碌等待,我们将拥有... else yield &gt;&gt; f r。显然,繁忙的循环首先通常是一个坏主意。一种替代方法是使用MVartakeMVar 来代替信号。
    • 对不起,也许我太密集了......为什么我需要分叉线程将控制权交还给主线程才能让putStrLn "writeIORef" 运行?我用+RTS -N 运行,用-threaded 编译;两个线程不应该...同时运行吗?
    • @jberryman 尝试在你的分叉线程循环中插入yield
    • 是的,我想这会奏效。在循环中添加putStrLn 修复了我尝试这样做时的行为,因此它肯定与没有分配的紧密循环有关。但我不明白为什么分叉线程需要暂停主线程才能继续
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-12
      • 2010-11-21
      • 1970-01-01
      • 2018-03-03
      • 2021-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多