質問

以下のコードを書いて気づいたのですが、 killThread ブロックされてもスレッドは継続します。これは forkProcess で実行した場合にのみ発生します。forkProcess を削除すると、すべてが期待どおりに動作します。

コード

{-# LANGUAGE TupleSections #-}
module Main where

import Control.Concurrent
import Control.Monad
import System.Posix.Process

{-# NOINLINE primes #-}
primes :: [Integer]
primes = 2:[x | x <- [3..], all (not . flip isDivisorOf x) (takeWhile (< truncate (sqrt $ fromInteger x :: Double)) primes)]
  where x `isDivisorOf` y = y `rem` x == 0

evaluator :: Show a => [a] -> IO ()
evaluator xs = do
  putStrLn "[Evaluator] Started evaluator."
  forM_ xs $ \x -> putStrLn $ "[Evaluator] Got result: " ++ show x
  putStrLn "[Evaluator] Evaluator exited."

test :: IO ThreadId
test = forkIO (evaluator $ filter ((== 13) . flip rem (79 * 5 * 7 * 3 * 3 * 2 * 3)) primes) -- Just some computation that doesn't finsish too fast

main :: IO ()
main = do
  pid <- forkProcess $ do
    a <- test
    threadDelay $ 4000 * 1000
    putStrLn "Canceling ..."
    killThread a
    putStrLn "Canceled"
  void $ getProcessStatus True False pid

出力

$ ghc test.hs -O -fforce-recomp -threaded -eventlog -rtsopts # I also tried with -threaded
$ ./test +RTS -N2  # I also tried without -N
[Evaluator] Started evaluator.
[Evaluator] Got result: 13
[Evaluator] Got result: 149323
[Evaluator] Got result: 447943
[Evaluator] Got result: 597253
[Evaluator] Got result: 746563
[Evaluator] Got result: 1045183
Canceling ...
[Evaluator] Got result: 1194493
[Evaluator] Got result: 1642423
[Evaluator] Got result: 1791733
[Evaluator] Got result: 2090353
[Evaluator] Got result: 2687593
[Evaluator] Got result: 3135523
[Evaluator] Got result: 3284833
[Evaluator] Got result: 4777933
[Evaluator] Got result: 5375173
^C[Evaluator] Got result: 5524483
^C

これは、メモリ割り当てがないために GHC のスレッド スケジューラが実行されないというよくある問題ではありません。プログラムを実行してそれを確認しました +RTS -sstderr, これは、ガベージ コレクターが非常に頻繁に実行されていることを示しています。Linux 64ビットでこれを実行しています。

役に立ちましたか?

解決

これ バグレポート とメモしています forkProcess ドキュメントにそのような記載がないにもかかわらず、子プロセスの非同期例外をマスクします。この動作は 7.8.1 のリリース時に修正されるはずです。

もちろん、非同期例外がマスクされている場合、 throw の中で killThread 無期限にブロックします。単純に行を削除すると、 main 含む forkProcess そして getProcessStatus, 、プログラムは意図したとおりに動作します。

module Main where

import           Control.Concurrent
import           Control.Monad
import           System.Posix.Process

{-# NOINLINE primes #-}
primes :: [Integer]
primes = 2:[ x | x <- [3..], all (not . flip isDivisorOf x) (takeWhile (< truncate (sqrt $ fromInteger x :: Double)) primes)]
  where x `isDivisorOf` y = y `rem` x == 0

evaluator :: Show a => [a] -> IO ()
evaluator = mapM_ $ \x ->
  putStrLn $ "[Evaluator] Got result: " ++ show x

test :: IO ThreadId
test = forkIO (evaluator $ filter ((== 13) . flip rem (79 * 5 * 7 * 3 * 3 * 2 * 3)) primes) -- Just some computation that doesn't finsish too fast

main :: IO ()
main = do
  a <- test
  threadDelay $ 4000 * 1000
  putStrLn "Canceling ..."
  killThread a
  putStrLn "Canceled"

で構築します ghc --make -threaded async.hs そして一緒に走ります ./async +RTS -N4.

何らかの理由で別のプロセスが必要な場合は、GHC 7.6.3 の子プロセスの非同期例外のマスクを手動で解除する必要があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top