Вопрос

Я написал приведенный ниже код и заметил, что 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, что показывает, что сборщик мусора работает очень часто.Я запускаю это на 64-битном Linux.

Это было полезно?

Решение

Это отчет об ошибке отмечает , что 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