Dan*_*iil 13 concurrency multithreading haskell
我有以下代码:
module Main where
import Data.IORef
import qualified Data.ByteString as S
import Control.Monad
import Control.Concurrent
main :: IO ()
main = do
var <- newIORef False
forkIO $ forever $ do
status <- readIORef var
if status
then putStrLn "main: file was read"
else putStrLn "main: file not yet read"
threadDelay 10000
threadDelay 200000
putStrLn ">>! going to read file"
--threadDelay 200000 --
str <- S.readFile "large2"
putStrLn ">>! finished reading file"
writeIORef var True
threadDelay 200000
Run Code Online (Sandbox Code Playgroud)
我编译代码并像这样运行:
$ ghc -threaded --make test.hs
$ dd if=/dev/urandom of=large bs=800000 count=1024
$ ./test +RTS -N3
<...>
main: file not yet read
main: file not yet read
main: file not yet read
main: file not yet read
>>! going to read file
>>! finished reading file
main: file was read
main: file was read
main: file was read
main: file was read
<...>
Run Code Online (Sandbox Code Playgroud)
也就是说,程序在读取文件时暂停.我发现这令人困惑,因为如果我readFile用threadDelay它替换产量控制正确.
这里发生了什么?GHC是否将forkIO代码映射到不同的系统线程?
(我使用的是Mac OS X 10.8.5,但人们在Ubuntu和Debian上报告了相同的行为)
小智 8
杰克是对的.
我相信大量分配会触发垃圾收集,但是在所有线程都准备就绪之前,集合本身无法启动.
遇到这样的问题时,可以使用ThreadScope查看发生了什么.
代码中的事件日志如下所示:

问题是我们想让其他线程有机会运行.因此S.readFile,我们不使用,而是使用分块读取并累积结果(或延迟字节串).如:
readChunky filename = withFile filename ReadMode $ \x -> do
go x S.empty
where
go h acc = do
more <- hIsEOF h
case more of
True -> return acc
False -> do
v <- S.hGet h (4096 * 4096)
go h $ S.append acc v
Run Code Online (Sandbox Code Playgroud)
它按预期工作.
见图:
.
我发展了一个理论.我相信大量分配会触发垃圾收集,但是在所有线程都准备就绪之前,集合本身无法启动.除读取文件之外的所有线程,直到读取完成,但不幸的是整个读取发生在一次调用中,因此需要一段时间.然后执行GC,之后一切都很好.
我也有一个解决方法,但我不认为它保证程序不会阻止(虽然我还没有阻止它,其他人报告它仍然在他们的机器上阻塞).运行以下命令+RTS -N -qg(如果允许并行GC,它有时会阻止,但不总是):
module Main where
import Data.IORef
import qualified Data.ByteString as S
import Control.Monad
import Control.Concurrent
main :: IO ()
main = do
done <- newEmptyMVar
forkIO $ do
var <- newIORef False
forkIO $ forever $ do
status <- readIORef var
if status
then putStrLn "main: file was read"
else putStrLn "main: file not yet read"
threadDelay 10000
threadDelay 200000
putStrLn ">>! going to read file"
--threadDelay 200000 --
_str <- S.readFile "large"
putStrLn ">>! finished reading file"
writeIORef var True
threadDelay 200000
putMVar done ()
takeMVar done
Run Code Online (Sandbox Code Playgroud)
我还没有关于为什么 GC等待系统调用的理论.我似乎无法用我自己的安全和不安全的绑定复制问题sleep并添加performGC到状态循环.