为什么putStrLn的换行符在线程锁之外?

5
当我在Haskell中使用putStrLn txt从多个线程中输出文本时,可能会出现换行符与文本交错的情况,但如果我使用putStr $ txt ++ "\n"则总是正常工作。
这样做是否正确?我做错了什么吗? 示例1:
thread 1: putStrLn "txt 1"
thread 2: putStrLn "txt 2"
thread 3: putStrLn "txt 3"
thread 4: putStrLn "txt 4"
thread 5: putStrLn "txt 5"

输出示例:

txt 1txt 3
txt 2txt 5txt 4

例子2:

thread 1: putStr $ "txt 1" ++ "\n"
thread 2: putStr $ "txt 2" ++ "\n"
thread 3: putStr $ "txt 3" ++ "\n"
thread 4: putStr $ "txt 4" ++ "\n"
thread 5: putStr $ "txt 5" ++ "\n"

始终为线程输出一行:

txt 1
txt 3
txt 2
txt 5
txt 4

谢谢

更新: 我正在使用ghc 6.12.3base-4.2.0.2


我无法在GHC 7.0.3上复现您描述的行为 - 我正在使用forkIO,尝试了线程和非线程运行时。此外,在http://hackage.haskell.org/packages/archive/base/latest/doc/html/src/GHC-IO-Handle-Text.html查看GHC实现时,似乎在处理这两种情况时没有真正的区别。 - Antti
1
@Antti:看起来在base 4.3.1.0中已经更改了。在base 4.3.0.0及之前,它的定义为putStrLn s = do putStr s; putChar '\n' - hammar
2个回答

7
据我所知,Haskell在putStrputStrLn等方面没有提供任何线程安全保证,因此即使像您所做的那样提前进行字符串连接,它也允许逐个字符地交错输出。
请参见Can I ensure that Haskell performs atomic IO?以了解如何正确同步您的I/O的示例。

谢谢,我一直在想锁是在putStrLn内部使用的。 - Zhen

3
根据 base 4.3.0.0 的定义 (putStrLn s = do putStr s; putChar '\n' - 感谢 hammar),我会认为 putStr 锁定了 stdout 句柄,因此能够在不插入任何内容的情况下输出字符串。这就解释了为什么使用 putStrLn 时输出会插入内容:在执行完 putStr 后锁被释放,然后另一个线程在执行 putChar 之前获取了该锁。不过这只是我的猜测。

无论如何,将 GHC 和它的基础库更新到 4.3.1.0 应该可以解决这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接