并行应用程序的可变与不可变[关闭]
-
23-08-2019 - |
题
在我正在编写的应用程序中,我需要编写大量基本类型,这些类型很可能是不可变的。但我想知道并行应用程序中可变类型与不可变类型的比较如何。
您可以对可变对象使用锁,对吧?它与并行应用程序中使用不可变类型的其他技术相比如何?
您至少不会使用不可变类型的锁,对吧?
解决方案
类型
- 尽可能使用不可变类型。
- 尽可能使用线程安全集合而不是显式锁。
- 仅当您没有其他合理选择时才使用可变类型。
线程数
- 尽可能使用线程池。
- 当线程池不可能时使用无限循环。
- 作为最后的手段,手动启动和停止线程。
如果您确实必须使用显式锁,请详细记录它们。特别是当涉及到锁定对象的顺序时。如果您知道 Foo 对象始终在 Bar 对象之前被锁定,并且 Foo(key 100) 始终在 Foo(key = 200) 之前被锁定,则不会出现死锁。
其他提示
要写入并行应用的关键是要远离可变共享状态程。线程之间共享可变状态需要同步它通常需要某种形式的锁定。使用不变类型可以确保您不会意外共享状态,通过使它不可能改变这些对象的状态。然而,这不是灵丹妙药,但只是一个设计选择。如果你正试图并行算法需要共享的状态,你将不得不建立某种形式的同步。
可变性不影响锁定。
当您使用可变类型时,您将面临“读后写”或“写后写”错误。这些是与在其他线程同时读取或更新该值时更新该值相关的同步错误。
为了防止同步错误,您必须使用某种形式的锁定机制。如果您确实使用显式锁定,则需要非常小心获取锁的顺序。如果不小心,可能会导致死锁。例如:线程 A 获取锁 X,然后线程 B 获取锁 Y。过了一会儿,线程 A 请求锁 Y,线程 B 请求锁 X。这会导致两个线程无限期地等待永远不会被释放的锁。
两个好的锁定经验法则:
- 按特定顺序获取锁(例如始终在锁定 Y 之前获取锁定 X)
- 保持锁的时间尽可能短。在需要时获取它们,并在使用完毕后立即释放它们。
如果您在创建对象后从未对其进行写入,则无需在访问它之前锁定它。因此,您不需要锁定不可变对象。
尽可能使用不可变类型。必要时使用可变类型(序列化等)。
使用 System.Threading.Tasks 进行所有并行化 - 当添加 async 和 wait 关键字时,甚至可以使用 C# 5 语言构建任务。
我写了一篇关于 C# 中可变/不可变类型的文章: http://rickyhelgesson.wordpress.com/2012/07/17/mutable-or-immutable-in-a-parallel-world/