13

次のコードがあります。

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  

コードをコンパイルして、次のように実行します。

$ 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
<...>

つまり、プログラムはファイルの読み取り中に一時停止します。readFileこれを置き換えるとthreadDelayコントロールが正しく生成されるため、これはわかりにくいと思います。

ここで何が起こっているのですか?GHCforkIOは別のシステム スレッドにコードをマッピングしていませんか?

(私は Mac OS X 10.8.5 を使用していますが、Ubuntu と Debian で同じ動作が報告されています)

4

3 に答える 3

5

私は理論を開発しました。大規模な割り当てがガベージ コレクションをトリガーしていると思いますが、すべてのスレッドの準備が整うまでコレクション自体を開始できません。ファイルを読み取るスレッドを除くすべてのスレッドは、読み取りが完了するまでブロックしますが、残念ながら読み取り全体が 1 回の呼び出しで行われるため、時間がかかります。その後、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

GC がシステムコールを待機している理由については、まだ理論がありません。私自身の安全なバインディングと安全でないバインディングをステータスループにsleep追加して問題を再現することはできないようです。performGC

于 2013-10-14T03:00:58.270 に答える
1

readFile根底にある操作ほどではないと思いますByteString。には、いくつかのunsafeFFI 呼び出しがありData.ByteString.Internalます。

foreign import ccall unsafe "string.h strlen" c_strlen
    :: CString -> IO CSize

foreign import ccall unsafe "static stdlib.h &free" c_free_finalizer
    :: FunPtr (Ptr Word8 -> IO ())

foreign import ccall unsafe "string.h memchr" c_memchr
    :: Ptr Word8 -> CInt -> CSize -> IO (Ptr Word8)

foreign import ccall unsafe "string.h memcmp" c_memcmp
    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt

foreign import ccall unsafe "string.h memcpy" c_memcpy
    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)

foreign import ccall unsafe "string.h memset" c_memset
    :: Ptr Word8 -> CInt -> CSize -> IO (Ptr Word8)

foreign import ccall unsafe "static fpstring.h fps_reverse" c_reverse
    :: Ptr Word8 -> Ptr Word8 -> CULong -> IO ()

foreign import ccall unsafe "static fpstring.h fps_intersperse" c_intersperse
    :: Ptr Word8 -> Ptr Word8 -> CULong -> Word8 -> IO ()

foreign import ccall unsafe "static fpstring.h fps_maximum" c_maximum
    :: Ptr Word8 -> CULong -> IO Word8

foreign import ccall unsafe "static fpstring.h fps_minimum" c_minimum
    :: Ptr Word8 -> CULong -> IO Word8

foreign import ccall unsafe "static fpstring.h fps_count" c_count
    :: Ptr Word8 -> CULong -> Word8 -> IO CULong

これらの安全でない呼び出しは、安全な呼び出しよりも高速ですが (各呼び出しのオーバーヘッドはほとんどありません)、完了するまで Haskell ランタイム システム (スレッドを含む) をブロックします。

これが遅延の原因であると 100% 確信しているわけではありませんが、最初に頭に浮かんだのはそれでした。

于 2013-10-13T18:00:33.767 に答える