我正在对并发性和内存可见性进行一些实验,并遇到了这种奇怪的行为(请参阅内联评论):
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
这里发生了什么事?
EDIT:所以我的机器有两个真正的核心和两个超线程核心,所以+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
仅在明确定义的安全点(仅在分配内存时)挂起线程。我相信您的分叉线程永远不会分配内存,因此它永远不会放弃对其他线程的控制。因此,一旦编译器安排了分叉线程(有时在您的程序的中间),您的主线程就永远不会继续进行。threadDelay
).
您可以了解更多有关安全点的信息here http://www.aosabook.org/en/ghc.html在“轻量级线程和并行性”部分中。
编辑:正如托马斯提到的,你可以使用Control.Concurrent.yield
当您遇到此类情况时,明确放弃控制权。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)