何时/为什么使用MVar而不是TVar

42

尽管MVar看起来更加简单,但我觉得TVar更容易使用,而且TVar的功能更加丰富。

所以我的问题很简单,什么情况下我需要使用MVar而非TVar?我想任何时候只要不需要事务更新,就可以使用MVar,但是这样有什么好处呢?

2个回答

47

MVar

  • 可能为空
  • 用于实现线程之间的同步模式
  • 允许线程之间单向通信
  • 在某些情况下可能比 TVar 更快

TVar

  • 可为空
  • 原子事务
  • 多个线程之间的“共享内存”; 可用于实现例如查找缓存,多个线程可以读/写 的数据结构
  • 访问时间与事务日志中操作的数量成正比
  • 长时间运行的事务容易受到饥饿的影响,如果存在许多短事务,则会阻止它们成功提交

IORef

  • 可变的指针类参考
  • 通常用于破坏性`IO`写入/更新
  • 具有原子CAS操作,但是复杂的事务逻辑更适合使用TVar

没有什么硬性规定来决定何时使用MVarTVar。 如果我所保护的资源有可能“丢失”(而不是为空,请考虑Nothing vs Just mempty),那么MVar通常是最合适的选择。如果我需要对资源执行原子块修改,则TVar最合适。


2
你提到了共享MVar用于线程通信,但是它们每个只应该用于单向通信,因为如果两个线程都在更新同一个MVar以进行双向通信,它们将会出现竞争条件。 - Jimmy Hoffa
你说得对。我并不是指两个线程会同时更新同一个“MVar”,我会让这一点更加清晰明确。 - cdk
4
可能需要添加一个关于饥饿的注释。MVar保证公平,而TVar则不保证。 - hammar
2
@JimmyHoffa,你可以使用MVar来实现多种模式,例如:一些线程放入数据,另一些线程取出数据(本质上是一个一元FIFO队列);许多线程使用modifyMVar修改共享状态;线程在进入临界区之前获取()并在退出时替换(将其用作锁)等。 - jberryman

41

TVars更加安全但速度较慢。

MVars可能会造成死锁,但效率非常高。

IORefatomicModifyIORef(CAS)的效率更高,但是它的用途受到严格限制。

这实际上是在安全性和性能之间做出的权衡。 TVars是完全通用的,非常安全。 其他所有方法都不如它安全,在一个递减的尺度上。


1
效率,够用就好。我认为它们的性能相似,所以故事的寓意是:如果需要事务更新,请使用TVar,否则请使用MVar,因为它会使您的多线程应用程序减速得更少。 - Jimmy Hoffa
4
有人能量化实际的速度差异吗?(可能难以测量,因为它取决于实际的线程交互......) - MathematicalOrchid
2
@MathematicalOrchid 忽略并发问题:读写 IORef 将是您最快的操作,但 GHC 7.8 随附了一些对 MVar 的改进,使得 takeMVar/putMVarread/writeIORef 相差仅有几个纳秒(例如在我的机器上为 9.7/4.6 vs 3.7/7/3(<通常的免责声明关于谎言和基准测试>)),而且我们还有一个新的非常快速的 readMVar(当 MVar 当然是满的)。 - jberryman
1
此外,由于某种原因,“atomicModifyIORef”在争用下表现非常糟糕。 - jberryman
3
我曾尝试对比 TVarsmodifyIORef 的性能。我做了一个银行账户程序,线程会在两个账户之间进行交易。使用 IORef 实现显然会出现不一致的情况。我第一次尝试对随机数生成器进行基准测试,第二次尝试对更快的随机生成器进行基准测试。第三次尝试中,我的随机生成器实际上并不随机,而是从一个数组中取预生成的数字,这时我发现自己不知道该怎么做,所以放弃了。 - monocell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接