【问题标题】:When/why use an MVar over a TVar何时/为什么在 TVar 上使用 MVar
【发布时间】:2013-03-04 14:43:54
【问题描述】:

我发现 TVar 很容易使用,尽管 MVar 看起来更简单,而 TVar 功能更丰富。

所以我的问题很简单,我想去 MVar 而不是 TVar 的条件是什么?我想在不需要事务更新的任何时候,我可以使用 MVar,但这对我有什么好处?

【问题讨论】:

    标签: haskell shared-memory stm


    【解决方案1】:

    MVar

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

    TVar

    • 可以不能为空
    • 原子事务
    • 线程之间的“共享内存”;例如,可以用来实现一个查找缓存,多个线程可以从中读取/写入
    • 访问是事务日志中操作次数的线性时间
    • 如果有许多较短的事务,长时间运行的事务很容易出现饥饿,从而阻止它们成功提交

    IORef

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

    对于何时使用MVarTVar,并没有真正的硬性规定。 如果我保护的资源永远“丢失”(而不是空的,考虑NothingJust mempty),那么MVar 通常是最有意义的。如果我需要对资源进行原子修改块,那么TVar 是最合适的。

    【讨论】:

    • 您指的是用于线程通信的共享 MVar,但它们不应该只用于单向通信吗,因为如果两个线程都在更新同一个 MVar 来来回交谈,它们将有比赛条件吗?
    • 你是对的。我并不是说两个线程都会更新相同的MVar,我会更清楚地说明这一点
    • 可能还想添加关于饥饿的注释。 MVar 保证公平,而 TVar 则不然。
    • @JimmyHoffa 您可以使用MVar 拥有几种模式,例如:一些线程让其他线程占用(本质上是一个 1 元 FIFO 队列);许多线程在做modifyMVar 来修改一些共享状态;线程在进入临界区之前采用() 并在退出时替换(将其用作锁)等。
    【解决方案2】:

    TVar 更安全但速度较慢。

    MVar 可能会死锁,但效率要高得多。

    更高效的仍然是IORefatomicModifyIORef (CAS),但是你可以用它做什么受到很大限制。

    这确实是一种安全而不是性能的权衡。 TVars是完全通用的,非常安全。其他一切都不那么重要,而且规模越来越小。

    【讨论】:

    • 效率,好'nuff。我认为它们具有相似的性能,因此故事的寓意是:如果您需要事务更新,请使用 TVar,否则使用 MVar 因为它会大大降低您的多线程应用程序的速度
    • 谁能量化实际的速度差异? (大概这很难衡量,因为它取决于实际的线程交互......)
    • @MathematicalOrchid 忽略并发问题:读取和写入 IORef 将是您最快的操作,但 GHC 7.8 对 MVar 进行了一些改进,将 takeMVar/putMVar 在几纳秒内read/writeIORef(比如我的机器上的 9.7/4.6 vs 3.7/7/3()),我们还有一个新的非常快的 readMVar(当然,当 MVar 已满时)。
    • 另外,atomicModifyIORef 在争用中的表现也很差,出于某种原因。
    • @MathematicalOrchid 我曾尝试对TVarsmodifyIORef 进行基准测试。我做了一些银行账户的事情,每个线程都会在两个银行账户之间进行交易。 IORef 的实现显然会出现不一致。我的第一次尝试使随机数生成器成为基准,而我的第二次尝试使一个更快的随机数生成器成为基准。在我的第三次尝试中,我得到了速度差异(~ 两倍快),我的随机生成器实际上根本不是随机的,而是从数组中获取预生成的数字。那时我意识到我不知道自己在做什么,所以我放弃了。
    猜你喜欢
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2020-12-22
    相关资源
    最近更新 更多