7

我正在考虑使用 TVar 在 Web 应用程序中存储一些状态(可以在重新启动时重新创建)。但是,我担心 TVar 的争用方面。似乎频繁的短时间运行的事务可以通过不断地中断较长的事务来饿死它们。此外,随着更多运行时间较长的事务不断重启,这将增加 CPU 的负载,从而进一步增加这些事务的长度。最终我觉得这可能会导致服务器完全没有响应。

考虑到这一点,我有以下问题:

(1) TVar(或其他数据类型)可以使用锁,而不是同时尝试/重试。

(2)TVar(或其他数据类型)能否有一些不同的争用机制,即“让事务在运行另一个事务之前运行一秒钟”,或者至少有一些保证事务最终会完成(即防止对运行时间更长的事务)。

4

3 回答 3

5

我认为没有办法保证饥饿自由,除非您更改 STM 系统本身的运行时代码。在我看来,引入锁以避免争用TVars首先违背了拥有 STM 的目的,因为使用 STM 的全部目的是摆脱经典的容易出错的基于锁的并发编程方法。

当然,饥饿可能会导致显着的性能损失,但前提是这样的大事务实际上是必要的。我试图记住的一个设计原则是TVars在低粒度级别上使用。例如,您可以使用更适合 STM 的数据结构,例如跳过列表 [1],而不是将整个Data.Map放入 aTVar中,这可能会导致每次更新条目时发生争用。

[1] http://hackage.haskell.org/package/tskiplist

于 2012-04-11T05:35:41.547 回答
3

仅当您有许多更新数据的廉价事务和读取数据的一些昂贵事务时,这才是一个问题。也许是对实时更新的数据集进行分析。

如果您确实对此感到担忧,请考虑使用标志 TVar。将其设置为 false 并在每次廉价交易开始时检查它是否为 false,retry否则调用。然后在进入您的长期事务之前将其设置为 true,并在退出时将其设置为 false。或者,您可以简单地将您的状态保护在TMVar. 您的长时间运行计算以原子方式获取 tmvar,执行其感觉,然后返回它。其他事务完全发生在一个实际的 STM 事务中。

还要记住,长时间运行的 STM 事务是一种棘手的野兽。由于懒惰,您可以廉价地将昂贵的值放入 var 中。您还可以非常快速地从一大堆变量中读取数据的“快照”。要进行真正长时间运行的事务,您需要从一大堆 var 中读取,然后根据您已阅读的内容,计算您将要写入新值(或从中读取值)的 var,以及计算本身一定很贵。很可能你甚至一开始都不在那种情况下!

于 2012-05-19T05:37:48.100 回答
0

这是对克林顿对彼得斯回答的评论之一的评论。但它变得很长。

考虑一下,您有两个银行账户:A 和 B。每个账户都有自己的锁保护。现在,您有两个事务,第一个是从 A 到 B 的资金转移,第二个是从 B 到 A 的事务。每个事务首先获取源帐户的锁定,然后获取目标帐户的锁定,转移资金并释放锁定。如果你不走运,这两个交易将陷入死锁,这两个帐户将永远不会做任何事情。如果您在 STM 中这样做,它们将相互追逐。如果你有无数的第一种,他们可能会饿死第二次交易。但是您仍然可以完成很多工作。在锁定的情况下,什么都不会发生。

STM 保证不存在与 TVar 的数据竞争!没有任何。通过锁定,您可以在非常仔细地检查您的代码后得出该结论。您添加的每一行都可能使您的结论完全无效。

于 2012-04-11T06:17:06.677 回答