首先,一些背景知识。我想要一个队列,我想以两种不同的模式之一操作。在第一种模式中,我希望能够检索一个元素(如果队列中存在一个元素),但是如果没有元素则不来阻止。在第二种模式中,我希望能够阻塞,直到队列中有一个元素。 (我知道我可以为每种模式使用专门的机制,但我想分解一些常用代码,因此如果我可以对两种操作模式使用相同的机制,那将是最简单的。)
我可以使用Chan
,但根据文档我不应该使用isEmptyChan
,因为它可能会因为死锁而被弃用。这让我留下了TChan
。 tryReadTChan
函数给出了我想要的第一个模式(即我可以检查元素是否存在而没有阻塞),但我不确定readTChan
究竟是什么。我的心智模型是atomically
块将继续重试,直到通道中存在一个元素,这意味着它将忙于循环浪费CPU周期;这与readChan
(即非STM版本)不同,它(如果我理解的话)将实际阻止线程的执行,直到元素可用,因为运行时线程调度程序理解了MVars。
TChan
和Chan
一样,如果我使用readTChan
,那么运行时是否足够聪明,直到值可用时才调度调用线程?或者它会浪费大量的CPU周期,不断轮询到值?
答案 0 :(得分:14)
STM阻塞(通过retry
)就好像它立即重试事务一样,但实现更聪明:由于STM会跟踪您在事务发生时读取的变量,因此它知道事务只要这些变量具有相同的值,它们的行为方式就会相同。因此,当事务失败时,它将阻止(实际上不会重试),直到您使用的其中一个变量发生更改。在TChan
s的情况下,这意味着它会阻止,直到有人写入TChan
。
我推荐Simon Marlow的并发和并行Haskell slides,以便对STM(以及其他内容)做一个很好的介绍。
答案 1 :(得分:11)
你的心智模型具有正确的指称语义,但是错过了STM的操作优化。当事务在STM中重试时,它将阻塞,直到在重试更改之前从中读取一些TVar
。 (是的,TChan
是根据TVar
实现的。)
因此,执行事务的意义与事务不断重试的意义相同 - 但是如果事务中没有任何事情发生变化,则STM系统足够聪明,不会忙于循环。