如何在Haskell中检索进程的输出而不阻塞线程
在不阻塞的情况下,向stdin写入数据并从子进程的stdout读取数据的最佳方式是什么 子流程是通过如何在Haskell中检索进程的输出而不阻塞线程,haskell,Haskell,在不阻塞的情况下,向stdin写入数据并从子进程的stdout读取数据的最佳方式是什么 子流程是通过System.IO.createProcess创建的,它返回用于写入和读取子流程的句柄。书写和阅读是以文本形式完成的 例如,我进行非阻塞读取的最佳尝试是超时1$hGetLine out,如果不存在要读取的行,它将返回仅“某行”或Nothing。然而,这对我来说似乎是一种黑客行为,所以我正在寻找一种更“标准”的方式 谢谢以下是一些示例,说明如何以@jberryman提到的方式与衍生流程交互 程序与脚
System.IO.createProcess
创建的,它返回用于写入和读取子流程的句柄。书写和阅读是以文本形式完成的
例如,我进行非阻塞读取的最佳尝试是超时1$hGetLine out
,如果不存在要读取的行,它将返回仅“某行”
或Nothing
。然而,这对我来说似乎是一种黑客行为,所以我正在寻找一种更“标准”的方式
谢谢以下是一些示例,说明如何以@jberryman提到的方式与衍生流程交互 程序与脚本
/compute
交互,该脚本以
的形式从stdin中读取行,并在延迟y秒后返回x+1。更多详情请访问
当与派生的进程交互时,有许多警告。为了避免“受到缓冲的影响”,您需要在发送输入时刷新传出管道,而派生的进程需要在每次发送响应时刷新标准输出。如果您发现stdout没有及时刷新,那么可以通过伪tty与进程交互
此外,示例假定关闭输入管道将导致生成进程终止。如果情况并非如此,则必须向其发送信号以确保终止
下面是示例代码-有关示例调用,请参见末尾的main
例程
import System.Environment
import System.Timeout (timeout)
import Control.Concurrent
import Control.Concurrent (forkIO, threadDelay, killThread)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
import System.Process
import System.IO
-- blocking IO
main1 cmd tmicros = do
r <- createProcess (proc "./compute" []) { std_out = CreatePipe, std_in = CreatePipe }
let (Just inp, Just outp, _, phandle) = r
hSetBuffering inp NoBuffering
hPutStrLn inp cmd -- send a command
-- block until the response is received
contents <- hGetLine outp
putStrLn $ "got: " ++ contents
hClose inp -- and close the pipe
putStrLn "waiting for process to terminate"
waitForProcess phandle
-- non-blocking IO, send one line, wait the timeout period for a response
main2 cmd tmicros = do
r <- createProcess (proc "./compute" []) { std_out = CreatePipe, std_in = CreatePipe }
let (Just inp, Just outp, _, phandle) = r
hSetBuffering inp NoBuffering
hPutStrLn inp cmd -- send a command, will respond after 4 seconds
mvar <- newEmptyMVar
tid <- forkIO $ hGetLine outp >>= putMVar mvar
-- wait the timeout period for the response
result <- timeout tmicros (takeMVar mvar)
killThread tid
case result of
Nothing -> putStrLn "timed out"
Just x -> putStrLn $ "got: " ++ x
hClose inp -- and close the pipe
putStrLn "waiting for process to terminate"
waitForProcess phandle
-- non-block IO, send one line, report progress every timeout period
main3 cmd tmicros = do
r <- createProcess (proc "./compute" []) { std_out = CreatePipe, std_in = CreatePipe }
let (Just inp, Just outp, _, phandle) = r
hSetBuffering inp NoBuffering
hPutStrLn inp cmd -- send command
mvar <- newEmptyMVar
tid <- forkIO $ hGetLine outp >>= putMVar mvar
-- loop until response received; report progress every timeout period
let loop = do result <- timeout tmicros (takeMVar mvar)
case result of
Nothing -> putStrLn "still waiting..." >> loop
Just x -> return x
x <- loop
killThread tid
putStrLn $ "got: " ++ x
hClose inp -- and close the pipe
putStrLn "waiting for process to terminate"
waitForProcess phandle
{-
Usage: ./prog which delay timeout
where
which = main routine to run: 1, 2 or 3
delay = delay in seconds to send to compute script
timeout = timeout in seconds to wait for response
E.g.:
./prog 1 4 3 -- note: timeout is ignored for main1
./prog 2 2 3 -- should timeout
./prog 2 4 3 -- should get response
./prog 3 4 1 -- should see "still waiting..." a couple of times
-}
main = do
(which : vtime : tout : _) <- fmap (map read) getArgs
let cmd = "10 " ++ show vtime
tmicros = 1000000*tout :: Int
case which of
1 -> main1 cmd tmicros
2 -> main2 cmd tmicros
3 -> main3 cmd tmicros
_ -> error "huh?"
导入系统环境
导入系统。超时(超时)
导入控制。并发
导入控制。并发(forkIO、threadDelay、killThread)
import Control.Concurrent.MVar(newEmptyMVar、putMVar、takeMVar)
导入系统。进程
导入系统.IO
--阻塞IO
main1 cmd tmicros=do
r返回x
x main2 cmd tmicros
3->main3命令tmicros
_->错误“嗯?”
您希望此函数具有什么语义?如果进程将字符“ABC”写入out
,然后您的程序调用hGetLineOnBlocking
,读取字符“ABC”,但无法返回只是
一些内容,因为尚未读取换行符;在出现更多类似于hGetLine
(显然)的字符之前,它无法阻止。你会扔掉那一部分吗?这几乎肯定是错误的。我怀疑,在这种情况下,你确实想阻止,等待其余的线。如果是这样,在使用hGetLine
@user2407038阅读之前,只需使用hReady
检查句柄是否为空。抱歉,我不理解您的评论,因为如果要读取整行,执行timeout 1$hGetLine…
总是返回just
内容,否则,它将返回Nothing
。为了让hGetLine
读取一行,它必须按顺序读取字符,直到到达换行符,然后返回。但是,如果hGetLine
读取了一组字符,但没有换行符,则该行未完成-因此hGetLine将阻塞,直到有更多字符为止。如果您的函数是合理的,它不会从缓冲区读取字符并丢弃它们,但它不能阻止-它对已读取的字符做什么?它是否返回不以换行符结尾的部分行?通常,您不会阻塞。相反,您生成一个线程,然后继续让它阻塞。另见。如果这解决了你的问题,我很乐意将其标记为副本;丹尼尔瓦格纳:埃里克的详细回答(见下文)更能回答我的问题。尽管如此,还是要感谢你提供了另一个问题的链接。非常感谢你的透彻回答:这是一个非常好的例子。只有几个问题:您不需要hClose outp
?另外,您没有执行terminateProcess phandle
,这是因为hClose inp
预计会关闭外部进程吗?如果生成的进程挂起,您的解决方案将如何强制它停止?