在 Simon Marlow 所著的《Haskell 中的并行和并发编程》中,第 7 章从第 125 页开始,其中有这样一个例子:
import Control.Concurrent
import Control.Monad
import System.IO
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
_ <- forkIO (replicateM_ 100000 (putChar 'A'))
replicateM_ 100000 (putChar 'B')
其输出如下
BBBBBBBABABABAAAAAAA
100000
(顺便说一下,这是我在上面的代码片段中更改10
并运行程序时真正得到的输出)。
在本章末尾,第 140 页,有如下评论(我强调):
[…] 这是公平性保证在实践中的一个例子。[…]。因此,这会导致两个线程之间完美地交替。交替模式被打破的唯一情况是,当一个线程没有持有 时,它被取消调度
MVar
。事实上,由于抢占,这种情况确实会不时发生,我们偶尔会在输出中看到一个由单个字母组成的长字符串。
MVar
根据我给出的输出,是的,我可以看到交替被破坏了,但我不太理解上面的解释。第一个例子中甚至没有。
您引用的同一段还包括
这就是 MVar 所在的位置:它是
stdout
。您可以查看Handle 的源代码来确认:至于如果其中一个线程被抢占,你如何才能获得完美的交错输出,我很难告诉你你正在读的章节里没有提到过的内容。被抢占的线程会暂时停止打印,因为它在没有持有任何锁的情况下被取消调度。在它再次被调度之前,另一个线程可以自由写入。