编辑:可以获得生成进程的 pid。您可以使用如下代码来执行此操作:
-- highly non-portable, and liable to change between versions
import System.Process.Internals
-- from the finalizer of the bracketed function
-- `ph` is a ProcessHandle as returned by createProcess
(\(_,_,_,ph) -> do
let (ProcessHandle pmvar) = ph
ph_ <- takeMVar pmvar
case ph_ of
OpenHandle pid -> do -- pid is a POSIX pid
... -- do stuff
putMVar pmvar ph_
如果你杀死进程,而不是打开ph_
在 mvar 中,您应该创建一个适当的ClosedHandle
然后把它放回去。重要的是,此代码要以屏蔽方式执行(括号将为您执行此操作)。
现在您已经有了 POSIX id,您可以根据需要使用系统调用或 shell out 来终止。如果你走这条路,请注意你的 Haskell 可执行文件不在同一个进程组中。
/结束编辑
这种行为似乎有点明智。的文档timeout
声称它对于非 Haskell 代码根本不起作用,事实上我没有看到它可以通用的任何方法。发生的事情是这样的readProcess
产生一个新进程,但在等待该进程的输出时超时。看起来readProcess
当异常中止时,不会终止生成的进程。这可能是一个错误readProcess
,或者可能是设计使然。
作为一种解决方法,我认为您需要自己实施其中的一些内容。timeout
通过在生成的线程中引发异步异常来工作。如果你把你的runOnExternalProgram
在异常处理程序中,您将得到您想要的行为。
这里的关键功能是新的runOnExternalProgram
,它是原始函数和readProcess
。制作一个新的会更好(更模块化、更可重用、更可维护)readProcess
当引发异常时,它会杀死生成的进程,但我将把它留作练习。
module Main where
import System.Timeout
import Criterion.Measurement
import System.IO.Unsafe
import System.Process
import Control.Exception
import System.IO
import System.IO.Error
import GHC.IO.Exception
import System.Exit
import Control.Concurrent.MVar
import Control.Concurrent
main = do
x <- time $ timeoutP (1 * 1000000) $ mytest 2
y <- getLine
putStrLn $ show x ++ y
timeoutP :: Int -> IO a -> IO (Maybe a)
timeoutP t fun = timeout t $ fun
mytest :: Int -> IO String
mytest n = do
x <- runOnExternalProgram $ n * 1000
return $ x ++ ". Indeed."
runOnExternalProgram :: Int -> IO String
runOnExternalProgram n =
-- convert the input to a parameter of the external program
let x = show $ n + 12
in bracketOnError
(createProcess (proc "sleep" [x]){std_in = CreatePipe
,std_out = CreatePipe
,std_err = Inherit})
(\(Just inh, Just outh, _, pid) -> terminateProcess pid >> waitForProcess pid)
(\(Just inh, Just outh, _, pid) -> do
-- fork a thread to consume output
output <- hGetContents outh
outMVar <- newEmptyMVar
forkIO $ evaluate (length output) >> putMVar outMVar ()
-- no input in this case
hClose inh
-- wait on output
takeMVar outMVar
hClose outh
-- wait for process
ex <- waitForProcess pid
case ex of
ExitSuccess -> do
-- convert the output as needed
let verboseAnswer = "External program answered: " ++ output
return verboseAnswer
ExitFailure r ->
ioError (mkIOError OtherError ("spawned process exit: " ++ show r) Nothing Nothing) )