如何在不阻塞Haskell中的线程的情况下从进程中获取输出 [英] How to retrieve output from a process without blocking the thread in Haskell

查看:162
本文介绍了如何在不阻塞Haskell中的线程的情况下从进程中获取输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



子进程是通过创建的, System.IO.createProcess 它返回写入和读取子进程的句柄。



例如,我在做非阻塞读取的最佳尝试是 timeout 1 $ hGetLine out ,如果不存在要读取的行,则返回只是某行 Nothing 。然而,这对我来说似乎是一种破坏,所以我正在寻找更标准的方式。



谢谢

解决方案

以下是一些如何以@jberryman提到的方式与衍生进程进行交互的示例。



< code>< x>的形式简单地从标准输入读取行。 < y> ,并在延迟 y 秒后返回 x + 1 此要点中的更多详细信息。



与生成的进程交互时有许多警告。为了避免遭受缓冲,每次发送输入时都需要刷新输出管道,并且每次发送响应时,产生的进程都需要刷新stdout。



另外,这些例子假设关闭输入管道将会导致输入管道被关闭终止产卵过程。如果不是这种情况,您将不得不发送一个信号以确保终止。



以下是示例代码 - 请参阅 main
$ b

  import System.Environment 
import System.Timeout(code>超时)
导入Control.Concurrent
导入Control.Concurrent(forkIO,threadDelay,killThread)
导入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 - 发送命令

- 阻止直到收到响应
contents < - hGetLine输出
putStrLn $got:++内容

hClose inp - 并关闭管道
putStrLnwai ting for process to terminate
waitForProcess phandle

- 非阻塞IO,发送一行,等待响应超时期间
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 - 发送命令,将在4秒后响应

mvar < - newEmptyMVar
tid < - forkIO $ hGetLine输出>> = putMVar mvar

- 等待响应超时时间
结果< - 超时tmicros(takeMVar mvar)
killThread tid

结果
无 - > putStrLn超时
只需x - > putStrLn $got:++ x

h关闭inp - 并关闭管道
putStrLn等待进程终止
waitForProcess phandle

- 非块IO,发送一行,每超时报告进度
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 - 发送命令

mvar< - newEmptyMVar
tid< - forkIO $ hGetLine outp>> = putMVar mvar

- 循环直到接收到响应;每个超时时间报告进度
let循环=执行结果< - 超时tmicros(takeMVar mvar)
结果
Nothing - > putStrLn仍在等待......>>循环
只需x - >返回x
x< - 循环
killThread tid

putStrLn $got:++ x

hClose inp - 并关闭管道
putStrLn等待进程终止
waitForProcess phandle

{ -

用法:./prog哪个延迟超时

其中
which =要运行的主例程:1,2或3
delay =发送到计算脚本的延迟秒数
timeout =等待响应的超时秒数

例如:

./prog 1 4 3 - 注意:main1
./prog 2 2 3 - 应该超时
./prog 2 4 3 - 应得到回应
./prog 3 4 1 - 应该看到仍在等待......几次

- }

main = do
(which:vtime:tout:_)< - fmap(map read)getArgs
let cmd =10++ show vtime
tmicros = 1000000 * tout ::诠释
案例哪
1 - > main1 cmd tmicros
2 - > main2 cmd tmicros
3 - > main3 cmd tmicros
_ - >错误嗯?


What is the best way to write to the stdin and read from the stdout of a subprocess without blocking?

The subprocess was created via System.IO.createProcess which returns handles for writing to and reading from the subprocess. The writing and reading is done in text format.

For example, my best attempt at doing non-blocking read is timeout 1 $ hGetLine out which returns a Just "some line" or Nothing if no line exists to be read. However, this seems an hack to me, so I am looking for a more "standard" way.

Thanks

解决方案

Here are some examples of how to interact with a spawned process in a fashion mentioned by @jberryman.

The program interacts with a script ./compute which simply reads lines from stdin in the form <x> <y> and returns x+1 after a delay of y seconds. More details at this gist.

There are many caveats when interacting with spawned processes. In order to avoid "suffering from buffering" you need to flush the outgoing pipe whenever you send input and the spawned process needs to flush stdout every time it sends a response. Interacting with the process via a pseudo-tty is an alternative if you find that stdout is not flushed promptly enough.

Also, the examples assume that closing the input pipe will lead to termination of the spawn process. If this is not the case you will have to send it a signal to ensure termination.

Here is the example code - see the main routine at the end for sample invocations.

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?"

这篇关于如何在不阻塞Haskell中的线程的情况下从进程中获取输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆