C# 并行应用程序的可变与不可变
在我正在编写的应用程序中,我需要编写许多基本类型,它们很可能是不可变的。但我想知道在并行应用程序中可变类型与不可变类型相比如何 可以对可变对象使用锁,对吗?它与并行应用程序中使用的不可变类型的其他技术相比如何 您至少不再使用不可变类型的锁,对吗?类型C# 并行应用程序的可变与不可变,c#,.net,parallel-processing,immutability,mutable,C#,.net,Parallel Processing,Immutability,Mutable,在我正在编写的应用程序中,我需要编写许多基本类型,它们很可能是不可变的。但我想知道在并行应用程序中可变类型与不可变类型相比如何 可以对可变对象使用锁,对吗?它与并行应用程序中使用的不可变类型的其他技术相比如何 您至少不再使用不可变类型的锁,对吗?类型 尽可能多地使用不可变类型 尽可能使用线程安全集合而不是显式锁 只有在没有其他合理选择时才使用可变类型 线程 尽可能多地使用线程池 当线程池不可能时,使用无止境循环 作为最后手段,手动启动和停止线程 如果必须使用显式锁,请完整记录它们。尤其是
- 尽可能多地使用不可变类型
- 尽可能使用线程安全集合而不是显式锁
- 只有在没有其他合理选择时才使用可变类型
- 尽可能多地使用线程池
- 当线程池不可能时,使用无止境循环
- 作为最后手段,手动启动和停止线程
如果必须使用显式锁,请完整记录它们。尤其是在锁定对象的顺序方面。如果您知道Foo对象总是在Bar对象之前锁定,并且Foo(key 100)总是在Foo(key=200)之前锁定,那么就不会出现死锁。编写并行化应用程序的关键是远离可变共享状态。在线程之间共享可变状态需要同步,这通常需要某种形式的锁定。使用不可变类型有助于确保您不会意外地共享状态,因为这样就不可能更改这些对象的状态。然而,这不是一个神奇的子弹,而是一个简单的设计选择。如果试图并行化的算法需要共享状态,则必须创建某种类型的同步
易变性不影响锁定。当您使用易变类型时,您将暴露自己在读后写或写后写错误中。这些是在其他线程同时读取或更新值时与更新值相关的同步错误 为了防止同步错误,必须使用某种形式的锁定机制。如果使用显式锁定,则需要非常小心获取锁的顺序。如果不小心,可能会导致死锁。例如:线程A获取锁X,然后线程B获取锁Y。而稍后线程A请求锁Y,线程B请求锁X。这会导致两个线程无限期地等待永远不会释放的锁 锁定有两个很好的经验法则:
- 按特定顺序获取锁(例如,始终在获取锁Y之前获取锁X)
- 保持锁的时间尽可能短。在需要的时候获取它们,并在使用完后立即释放它们
如果在对象创建后从未写入对象,则无需在访问它之前锁定它。因此,您将不需要锁定不可变对象。尽可能使用不可变类型。必要时使用可变类型(序列化等) 使用System.Threading.Tasks进行所有并行化—当添加async和await关键字时,任务甚至将内置在C#5语言中
我写了一篇文章,关于C(4)中的易变/不可变类型,如果你在.NET中,考虑使用任务并行库。还有一个线程池,只是一个非常聪明的线程池。我听说.NET的ThreadPool类正在被淡化,我强烈建议观看Rich Hickey的演示:一些关于并行应用程序的不变性、身份、状态和锁定的惊人见解。