写入相同的内存位置,这可能吗?

Ach*_*les 0 memory multithreading

考虑以下:

ThreadA和ThreadB是将诊断信息写入存储诊断信息列表的公共对象的两个线程.ThreadA和ThreadB是否可以同时写入相同的内存地址?如果是这样会导致什么?

我正在使用.NET但是我不一定对一个特定的语言特定答案感兴趣.

Car*_*rum 5

"同一时间"只能达到一定的粒度 - 在某些时候,对存储器阵列的实际写入将被序列化.那时,该地址的值将是最近发生的任何写入的值.你可以在多处理器系统上有一些有趣的行为.如果每个线程在不同的处理器上运行,每个处理器都有自己的缓存,则每个线程可能只看到自己写入的结果,甚至从未知道尝试过的其他线程.


joh*_*y g 5

腐败

不管系统[并发或真正并行],存储器的状态取决于存储器设备的实现.一般来说,内存读取和写入不是原子的,这意味着对同一内存地址的多次并发访问可能会返回不一致的结果[即数据损坏].

想象一下两个并发请求,1个写入,1个读取,一个简单的整数值.假设一个整数是4个字节.我们还要说,读取需要2ns才能执行,写入需要4ns才能执行

  • t0,底层4字节元组的初始值,[0,0,0,0]
  • t1,写操作开始,写第一个字节[255,0,0,0]
  • t2,写操作继续,写第二个字节[255,255,0,0]
  • t2,读操作开始,读取前2个字节[255,255, - , - ]
  • t3,写操作继续,写第三个字节[255,255,255,0]
  • t3,读操作结束,读取最后2个字节[255,255,255,0]
  • t4,写操作结束,写第四个字节[255,255,255,255]

read返回的值既不是原始值也不是新值.该值已完全损坏.

这对你意味着什么!

不可否认,这是一个令人难以置信的简化和人为的例子,但这会对你的场景产生什么影响?在我看来,诊断系统中最脆弱的部分是诊断数据列表.

如果你的列表是固定大小的,比如一个对象的引用数组,最好你可能丢失整个对象,因为数组元素被竞争线程覆盖,最坏的情况是如果元素包含损坏的对象引用则会出现错误[腐败情形以上].

如果您的列表是动态的,那么底层数据结构可能会被破坏[如果一个数组在.Net List<>中重新分配时,或者一个链接列表你的下一个\ prev引用丢失或损坏].

作为旁白

为什么内存访问不是原子的?出于同样的原因,基本集合实现不是原子的 - 它会限制太多并引入开销,从而有效地惩罚了简单的场景.因此,消费者[us!]需要同步我们自己的内存访问.