[英]Haskell: How does TVar work?
TVar 是如何工作的? 根据我的阅读,它试图在收到所有事务后立即运行它们,但是,完成的事务会使其他当前正在运行的事务无效,然后必须重新启动。 这是 TVar 的工作原理吗?
如果是这种情况,如果每 100 毫秒发生 1 毫秒长的事务,是否意味着需要 200 毫秒处理的事务永远不会完成?
只要两个事务访问不同的TVars
,它们就可以同时提交而不会相互无效。
为了弄清楚交易何时无效,让我们考虑以下场景:
t:: TVar Int
初始化为0
,并在事务A
的开始通过readTVar t
读取。B
,其中执行writeTVar t 1
。 假设B
在A
之前提交。 STM系统会检查是否有不一致,并断定B
此时commit是安全的,所以现在writeTVar t 1
生效。A
无效,因为在A
的开头读取了t
的旧值0
。 (如果允许A
提交,我们就会违反原子性。)关于 Haskell 的 STM 系统的原始论文 [1](参见第 6.5 节)回答了您的问题:
“饥饿是可能的。例如,运行时间很长的事务可能会反复与较短的事务发生冲突。我们认为实践中不太可能发生饥饿,但如果没有进一步的经验,我们无法判断。”
[1] 蒂姆·哈里斯、西蒙·马洛、西蒙·佩顿·琼斯和莫里斯·赫利希。 ACM 并行编程原则与实践会议 2005 (PPoPP'05)。
如果每 100 毫秒发生 1 毫秒长的事务,是否意味着需要 200 毫秒处理的事务永远不会完成?
事务只有在触及相同的TVar
时才会发生冲突,因此如果一些 1ms 事务避免了 200ms 事务影响的所有变量,那么 200ms 事务就可以完成。 此外,由于STM
monad 对内部允许的内容非常严格(仅memory 次访问和纯计算;)事务长度之间存在这种差异是非常不寻常的,通常,它们只有几个 memory 读/写长,并且所有IO
和其他计算都将在交易之外完成。 此外,一个特定的事务是否曾经被其他事务永远阻塞是一个调度问题; 我不是 100% 确定 GHC 当前的调度程序是什么样的,但它似乎有理由优先考虑较旧(或更高故障率)的事务。
也就是说,活锁是STM
的一个非常现实的问题,并且在更传统的锁定并发实现中与死锁一样隐蔽且难以推理。
TVar 是如何工作的?
您可能会喜欢这篇论文:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.