【问题标题】:Mutable vs Immutable for parallel applications [closed]并行应用程序的可变与不可变 [关闭]
【发布时间】:2012-07-17 23:04:50
【问题描述】:

在我正在编写的应用程序中,我需要编写很多基本类型,它们很可能是不可变的。但我想知道并行应用程序中的可变类型与不可变类型的比较。

您可以对可变对象使用锁,对吗?它与并行应用程序中用于不可变类型的其他技术相比如何?

您至少远离使用不可变类型的锁,对吧?

【问题讨论】:

标签: c# .net parallel-processing immutability mutable


【解决方案1】:

类型

  • 尽可能使用不可变类型。
  • 尽可能使用线程安全集合而不是显式锁。
  • 只有在没有其他合理选择时才使用可变类型。

线程

  • 尽可能使用线程池。
  • 当线程池不可用时使用无限循环。
  • 作为最后的手段,手动启动和停止线程。

如果您必须使用显式锁,请完整记录它们。尤其是涉及到锁定对象的顺序时。如果你知道 Foo 对象总是在 Bar 对象之前被锁定并且 Foo(key 100) 总是在 Foo(key = 200) 之前被锁定,你就不会遇到死锁。

【讨论】:

  • 如果您使用的是 .Net 4.0,请考虑使用任务并行库。
  • 仍然是一个线程池,只是一个更智能的线程池。我听说 .NET 的 ThreadPool 类正在被贬低以支持它。
【解决方案2】:

编写可并行化应用程序的关键是远离可变共享状态。在线程之间共享可变状态需要同步,这通常需要某种形式的锁定。使用不可变类型可以帮助确保您不会意外共享状态,因为无法更改这些对象的状态。然而,这不是灵丹妙药,而只是一种设计选择。如果您尝试并行化的算法需要共享状态,您将不得不创建某种同步。

可变性不影响锁定。

【讨论】:

    【解决方案3】:

    当您使用可变类型时,您会将自己暴露在读后写或写后写错误中。这些是与更新值相关的同步错误,而其他线程正在同时读取或更新该值。

    为了防止同步错误,您必须使用某种形式的锁定机制。如果您确实使用显式锁定,则需要非常小心获取锁的顺序。如果你不小心,你可能会引入死锁。例如:线程 A 获取锁 X,然后线程 B 获取锁 Y。稍后,线程 A 请求锁 Y,线程 B 请求锁 X。这导致两个线程无限期地等待永远不会释放的锁。

    两个很好的锁定经验法则:

    • 按特定顺序获取锁(例如,始终在锁 Y 之前获取锁 X)
    • 保持锁定的时间尽可能短。在需要时获取它们,并在完成后立即释放它们。

    如果您在创建对象后从未对其进行写入,则无需在访问之前锁定它。因此,您不需要锁定不可变对象。

    【讨论】:

      【解决方案4】:

      尽可能使用不可变类型。必要时使用可变类型(序列化等)。

      使用 System.Threading.Tasks 进行所有并行化 - 添加 async 和 await 关键字后,甚至可以使用 C# 5 中的语言构建任务。

      我写了一篇关于 C# 中可变/不可变类型的文章:http://rickyhelgesson.wordpress.com/2012/07/17/mutable-or-immutable-in-a-parallel-world/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-24
        • 2013-11-29
        • 2021-01-16
        相关资源
        最近更新 更多