为什么不可变对象是线程安全的?

时间:2010-08-29 14:28:54

标签: c# .net clr thread-safety immutability

class Unit {
    private readonly string name;
    private readonly double scale;

    public Unit(string name, double scale) {
        this.name = name;
        this.scale = scale,
    }

    public string Name { get { return name; } }
    public string Scale { get { return scale; } }

    private static Unit gram = new Unit("Gram", 1.0);

    public Unit Gram { get { return gram; } }
}

多个线程可以访问Unit.Gram。为什么多个线程可以同时读取Unit.Gram.Title

我担心的是他们指的是相同的内存位置。一个线程开始读取该内存,那么它是不是“被锁定”了呢? .NET是否处理下面这个关键部分的同步?或者我错误地认为同步阅读需要同步?

8 个答案:

答案 0 :(得分:58)

是什么让对象不是线程安全的?如果在线程正在读取该对象的值/状态时,该对象不是线程安全的。如果第二个线程在第一个线程读取它时更改了该对象的值,则通常会发生这种情况。

根据定义,不可变对象不能更改值/状态。由于每次读取不可变对象时它都具有相同的值/状态,因此可以让任意数量的线程读取该对象而不用担心。

答案 1 :(得分:12)

我认为你的问题不是关于线程安全性或不可靠性,而是关于内存访问的(非常)低级细节。

这是一个沉重的主题,但简短的回答是:是的,两个线程(更重要的是,2 + CPU)可以同时读取(和/或写入)同一块内存。

只要该内存区域的内容不可变,所有问题都解决了。当它可以更改时,会出现一系列问题,volatile关键字和Interlocked类是我们用来解决这些问题的一些工具。

答案 2 :(得分:11)

同时读取需要同步。由于只需要编写器(或读者至少一个编写器)进行同步,因此不可变对象不需要同步,因此线程安全。

答案 3 :(得分:4)

如果对象是不可变的,则其状态永远不会改变。因此,陈旧数据的担忧就会消失。线程读取永远不会被锁定,所以这是一个非问题(死锁)

答案 4 :(得分:3)

在绝大多数情况下,并发读取不需要同步(例外情况是内存映射IO,其中从某个地址读取会导致副作用)。

如果并发读取确实需要同步,那么编写有用的多线程代码几乎是不可能的。例如,要执行一段代码,处理器必须读取指令流,如果函数本身必须防止自身同时执行,您将如何编写锁定函数:)?

答案 5 :(得分:2)

只能通过特定线程在一个CPU周期内读取相同的内存位置。在这种情况下读取的顺序无关紧要,因为基础值不会改变。因此,读取不可能不一致,因此在这种情况下不需要任何级别的同步。

答案 6 :(得分:1)

Memory Management Units是处理内存读取的处理器的一部分。如果你有一个以上,一旦在蓝色的月亮中,其中2个可能尝试在相同的十几纳秒内读取相同的记忆位置,但没有问题导致看到他们得到相同的答案。

答案 7 :(得分:1)

除了例如驱动程序的映射内存之外的异常,两个线程同时读取相同的内存地址没有问题。当一个线程执行一些写入数据时,可能会出现问题。在这种情况下,可能会阻止其他线程读取该对象/数据。

但问题不在于文章的同时性(在最低电子级别,它们一个接一个地出现),问题在于对象/数据集可能会丢失他们的一致性通常使用section critic来隔离一些其他线程可能无法同时读取/写入的代码。

网上有很多例子,但请考虑以下几点,price是一个类的私有成员,比如Product,它也有2个方法

public void setPrice(int value) {
  price = value;
  // -- point CRITIC --
  price += TAX;
}

public int getPrice() {
  return price;
}

setPrice(v)将产品的价格设置为v,并使用增值税进行调整(程序应该有value += TAX; price = value,但这不是重点: - )

如果线程A写入价格100,TAX是(固定)1,则产品价格最终将设置为101.但是如果线程B通过getPrice()读取价格而线程A位于{{1} }?返回B的价格将错过TAX,并且是错误的。

setPrice()应位于关键部分(point CRITIC)内,以防止在设置价格期间对对象的任何访问

lock
相关问题