打印语句未实时执行

Print statements not being executed in real time

提问人:string_loginUsername 提问时间:4/8/2023 更新时间:4/9/2023 访问量:61

问:

我有一个包装器,每当返回非零退出代码时,它都会提前退出,并返回输出readCreateProcessWithExitCodestdout

import System.Process

runShell :: String -> String -> IO String
runShell cmd msg = do
  (ec, r, err) <- readCreateProcessWithExitCode (shell cmd) ""
  when (isFailure ec) (error $ msg <> ": " <> err)
  return r

从文档页面来看,似乎类似于更简单的,它声称会阻止,直到分叉进程终止。readCreateProcessWithExitCodereadProcess

现在,每当我尝试使用 .stdoutputStrLn

fn :: IO ()
fn = do
  void $ runShell "lightweight" "err1"
  print "log1"
  void $ runShell "expensive" "err2"
  print "log2"

假设两个命令执行都成功,并且第二个命令需要几秒钟才能执行,则该函数应在运行后几乎立即记录,等待几秒钟,然后打印 .然而,事实并非如此 - 日志似乎被“暂停”,并且仅在程序执行结束时一次性打印。"log1"lightweight"log2"

我以为懒惰的评估与此有关,但明确评估结果也无济于事putStrLn

() <- putStrLn "log1"

Haskell,IO

评论

2赞 willeM_ Van Onsem 4/8/2023
我不确定这是懒惰的评估。这可能只是对结果的缓冲。根据 shell、操作系统和处理程序的不同,将数据写入 stdout 本身不会立即在 shell 上打印。
1赞 string_loginUsername 4/8/2023
@WillemVanOnsem 你说得很对。 似乎已经解决了这个问题hSetBuffering stdout NoBuffering
1赞 chi 4/9/2023
请记住,如果输出很大,禁用缓冲可能会对性能产生负面影响。考虑在 s 之后运行(另外,如果您不希望字符串被引用和转义,请改用)hFlush stdoutprintputStrLn "..."

答:

3赞 string_loginUsername 4/8/2023 #1

正如 Willem Van Onsem 在评论中指出的那样,默认情况下会进行一些缓冲。可以使用以下方法禁用它stdout

hSetBuffering stdout NoBuffering

评论

0赞 Daniel Wagner 4/15/2023
LineBuffering在效率和及时性之间可能比 .它是 stdout 的默认值,除非您以不寻常的方式运行程序,例如作为管道的一部分。NoBuffering