假设我有以下代码:
void* p0 = nullptr;
void* p1 = alloc_some_data();
void f1() {
p0 = p1;
p1 = nullptr;
}
假设{1}在线程1上运行。是否有可能(保留代码)另一个线程可能在某个时刻将f1
和p0
视为{{1} (如果编译器或硬件重新排序指令,如第二次赋值发生在第一次之前)?
我问这个的原因是因为我想实现一个垃圾收集器,我想知道我是否需要使用原子指令(p1
)从GC线程访问指针。如果GC线程看到nullptr
没有问题,但是如果GC线程看到std::atomic
则会出现问题,因为它会先前在p1中将数据报告为无法访问时显而易见。
答案 0 :(得分:2)
如果您在一个线程中读取一个由另一个线程写入而没有同步的对象,则会有数据争用。这显然意味着您的垃圾收集器需要使用某种同步来读取值。关于你的原始问题:你的代码中没有任何内容表明在p0
的写入之前写入p1
变得可见,即另一个线程确实可以看到两者都为空。这与用于与另一个线程通信的同步原语无关:这两个写操作之间没有排序。
答案 1 :(得分:0)
是。虽然不一定可能,但完全有可能,因为这些操作不是原子的。
一个(少数可能的情况)是:
Thread 2: Get value of p0 (null)
Thread 1: Get value of p1 (non-null)
p0 = p1
p1 = nullptr
Thread 2: Get value of p1 (null)
您需要使用某种形式的访问控制(即互斥锁)。
答案 2 :(得分:0)
您的问题的答案是肯定的,但它依赖于编译器和CPU。我想你还需要让p0
和p1
变得不稳定。要停止重新排序,您可以使用_mm_sfence
和_mm_lfence
instrinsics(适用于x86 / x64)