我正在考虑使用TVar在web应用程序中存储一些状态(可以在重启时重新创建)。然而,我关心的是TVar的争用方面。看起来,频繁的短时间运行的事务可以通过不断地中断长时间的事务来使它们饿死。此外,随着更多长时间运行的事务不断重新启动,这将增加CPU上的负载,从而进一步增加这些事务的长度。最终,我觉得这可能会导致服务器完全没有响应。
考虑到这一点,我有以下问题:
(1) TVar (或其他数据类型)是否可以使用锁,而不是同时尝试/重试。
(2) TVar (或另一种数据类型)可以有一些不同的争用机制,即“让事务运行一秒钟,然后再运行另一个事务”,或者至少有一些保证事务最终会完成的竞争机制(即防止starvation运行更长时间的事务的争用算法)。
发布于 2012-05-19 13:37:48
只有当您有许多更新数据的廉价事务和一些读取数据的昂贵事务时,这才是一个问题。也许是在实时更新的数据集上进行分析。
如果您确实关心这一点,请考虑使用标志TVar。将其设置为false,并在每个廉价事务开始时检查它是否为false,否则将调用retry。然后,只需在进入长时间运行的事务之前将其设置为true,并在退出时将其设置为false。或者,您可以简单地在TMVar后面保护您的状态。您的长时间运行的计算以原子方式获取tmvar,按感觉执行,然后返回它。其他事务完全发生在单个实际的STM事务中。
还要记住,长时间运行的STM事务是一种棘手的野兽。由于懒惰,您可以将昂贵的值廉价地放入var中。您还可以非常快速地从一大堆变量中读取数据的“快照”。要拥有一个真正的长时间运行的事务,您需要从一大堆变量中读取,然后根据您读取的内容,计算您将向其中写入新值(或从中读取值)的变量,而这种计算本身肯定是非常昂贵的。所以很可能你一开始就不在那个场景中!
发布于 2012-04-11 13:35:42
我不认为有办法保证饥饿自由,除非你改变STM系统本身的运行时代码。在我看来,引入锁来避免TVars之间的争用首先违背了拥有STM的目的,因为使用STM的全部目的是为了摆脱经典的、容易出错的、基于锁的并发编程方法。
当然,饥饿可能会导致显著的性能损失,但前提是这样的大型事务实际上是必要的。我试图记住的一个设计原则是在低粒度级别使用TVars。例如,您可以使用更友好的Data.Map数据结构,比如Skiplists1,而不是将整个TVar放入一个STM中,这可能会在每次更新条目时引起争用。
1
发布于 2012-04-11 14:17:07
这篇文章是对克林顿关于彼得斯回答的评论之一的评论。但它变得太长。
考虑一下,您有两个银行帐户:A和B。每个帐户都有自己的锁。现在,您有两个事务,第一个将资金从A转移到B,第二个从B转移到A。每个事务先获取源帐户的锁,然后再获取目标帐户的锁,然后转移资金并释放锁。如果您不走运,这两个事务将进入死锁,这两个帐户将永远不会做任何事情。如果你在STM中这样做,它们将会互相追赶。如果你有无限多的第一类,他们可能会饿死第二类交易。但是你仍然可以做很多事情。而在锁定的情况下什么都不会发生。
STM保证不会与TVars发生数据竞争!什么都没有。有了锁,你可以在仔细检查你的代码后得出这个结论。你添加的每一行都可能使你的结论完全无效。
https://stackoverflow.com/questions/10099990
复制相似问题