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