haskell - fork 的 IORef 读取器函数似乎会停止主线程

标签 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 个功能。根据 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

最佳答案

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

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

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

关于haskell - fork 的 IORef 读取器函数似乎会停止主线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25597124/

相关文章:

Haskell 函数::[名称] -> [[(名称, Bool)]]

haskell - 如何使用堆栈在 ghci 中加载测试

haskell - 即使我的应用程序不访问旧数据,GHC 运行时也会访问旧数据吗?

iphone - 可以从其他后台线程启动后台线程吗? (NSObj)

java - 异步迭代器

haskell - 从类型列表中获取常规列表

haskell - RankNTypes : apply the same function to pairs of different types

haskell - 在 Haskell 中观察惰性

multithreading - SDL-Mixer 音频在启动 Reactive-Banana 输入循环时停止

sql - 以原子方式标记并返回数据库中的一组行