Data.ByteString.readFile是否阻止所有线程?

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)

也就是说,程序在读取文件时暂停.我发现这令人困惑,因为如果我readFilethreadDelay它替换产量控制正确.

这里发生了什么?GHC是否将forkIO代码映射到不同的系统线程?

(我使用的是Mac OS X 10.8.5,但人们在Ubuntu和Debian上报告了相同的行为)

小智 8

杰克是对的.

我相信大量分配会触发垃圾收集,但是在所有线程都准备就绪之前,集合本身无法启动.

遇到这样的问题时,可以使用ThreadScope查看发生了什么.

代码中的事件日志如下所示:

img non-chunked.png

问题是我们想让其他线程有机会运行.因此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)

它按预期工作.

见图: 见图表.


Jak*_*hur 5

我发展了一个理论.我相信大量分配会触发垃圾收集,但是在所有线程都准备就绪之前,集合本身无法启动.除读取文件之外的所有线程,直到读取完成,但不幸的是整个读取发生在一次调用中,因此需要一段时间.然后执行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到状态循环.