4

我想创建一个进程并将我的haskell程序中的一些文本定期写入进程的stdin(来自IO操作)。

以下内容在 GHCi 中正常工作,但在构建和运行时无法正常工作。在 GHCi 中,一切都运行良好,并且定期输入 IO 操作的值。但是,当构建并运行时,在写入进程的标准输入时,它似乎会暂停任意长时间。

我已经使用CreateProcess(from System.Process) 创建句柄并尝试过hPutStrLn(缓冲区设置为NoBuffering--LineBuffering也不起作用)。

所以我正在尝试这个process-streaming包,pipes但似乎根本没有任何工作。

真正的问题是:我如何从 haskell 创建一个进程并定期写入它?

展示此行为的最小示例:

import System.Process
import Data.IORef
import qualified Data.Text as T  -- from the text package
import qualified Data.Text.IO as TIO
import Control.Concurrent.Timer  -- from the timers package
import Control.Concurrent.Suspend -- from the suspend package

main = do
    (Just hin, _,_,_) <- createProcess_ "bgProcess" $
        (System.Process.proc "grep"  ["10"]) { std_in = CreatePipe }

    ref <- newIORef 0 :: IO (IORef Int)
    flip repeatedTimer (msDelay 1000) $ do
        x <- atomicModifyIORef' ref $ \x -> (x + 1, x)
        hSetBuffering hin NoBuffering
        TIO.hPutStrLn hin $ T.pack $ show x

任何帮助将不胜感激。

4

2 回答 2

3

Producer这是一个发出带有第二个延迟的数字序列的管道:

{-# language NumDecimals #-}
import Control.Concurrent
import Pipes
import qualified Data.ByteString.Char8 as Bytes

periodic :: Producer Bytes.ByteString IO ()
periodic = go 0
    where
        go n = do
            d <- liftIO (pure (Bytes.pack (show n ++ "\n"))) -- put your IO action here
            Pipes.yield d
            liftIO (threadDelay 1e6)
            go (succ n)

并且,使用process-streaming,我们可以将生产者提供给外部流程,如下所示:

import System.Process.Streaming

main :: IO ()
main = do
    executeInteractive (shell "grep 10"){ std_in = CreatePipe } (feedProducer periodic)

我用过executeInteractive,它会std_in自动设置为NoBuffering.

此外,如果您使用管道std_out并希望立即处理每个匹配项,请务必将--line-buffered选项传递给 grep(或使用stdbuf命令)以确保匹配项在输出中立即可用。

于 2016-05-25T21:59:55.730 回答
0

怎么样使用threadDelay,例如:

import Control.Monad (forever)
import Control.Concurrent (threadDelay)
...

forever $ do
    x <- atomicModifyIORef' ref $ \x -> (x + 1, x)
    hSetBuffering hin NoBuffering
    TIO.hPutStrLn hin $ T.pack $ show x
    threadDelay 1000000  -- 1 sec

如果您需要同时做其他工作,请在另一个线程中生成它。

您可以通过以下方式删除他对 IORef 的需求:

loop h x = do 
    hSetBuffering h NoBuffering
    TIO.hPutStrLn h $ T.pack $ show x
    threadDelay 1000000
    loop h (x+1)

而且,当然,您只需要执行hSetBuffering一次 - 例如,在您进入循环之前执行此操作。

于 2016-05-25T21:45:01.210 回答