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是否处理下面这个关键部分的同步?或者我错误地认为同步阅读需要同步?
答案 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