C 构造函数存储器同步



假设我有类似的代码:

void InitializeComplexClass(ComplexClass* c);
class Foo {
 public:
  Foo() {
    i = 0;
    InitializeComplexClass(&c);
  }
 private:
  ComplexClass c;
  int i;
};

如果我现在做类似Foo f;之类的事情,然后将f指针移交给另一个线程,那么我可以保证InitializeComplexClass()完成的任何商店对执行访问f的其他线程的CPU可以看到吗?将零写入i的商店呢?我是否必须在班级中添加一个哑光,在构造函数中锁上一个作者锁,然后以任何访问成员的方法来键入相应的读取器锁?

更新:假设构造函数返回后,我将指针移交给其他许多线程。我不是假设代码在X86上运行,而是可以在PowerPC之类的诸如PowerPC之上运行,该功率可以自由进行内存重新排序。我对构造函数返回时必须将编译器必须注入代码中的哪种内存屏障基本上感兴趣。

为了使另一线程能够了解您的新对象,您必须以某种方式交出对象/信号。用于向线程发出信号,您写入内存。X86和X64都按顺序执行所有内存写作,CPU不会相互重新排序这些操作。这称为"Total Store Ordering",因此CPU写入队列的作品类似于"首先出现"。
鉴于您首先创建一个对象,然后然后将其传递到另一个线程,因此对内存数据的这些更改也将按顺序进行,另一个线程将始终以相同的顺序看到它们。到另一个线程了解新对象时,该对象的内容甚至可以更早地用于该线程(如果线程以某种方式知道在哪里看)。
总而言之,这次您不必同步任何内容。在之后交出对象是您需要的所有同步。

更新:在非TSO体系结构上,您没有此TSO保证。因此,您需要同步。使用MemoryBarrier()宏(或任何联锁操作)或某些同步API。通过相应的API原因向其他线程发出同步,否则不会同步API。


x86和x64 CPU可能会重新计算过去的读物,但这在这里无关紧要。只是为了更好地理解 - 可以在阅读后订购写作,因为写入记忆的写入队列和潮红可能需要一些时间。另一方面,读取缓存始终与其他处理器的最新更新一致(它们已经通过了自己的写入队列)。

这个话题变得如此令人难以置信地使人感到困惑,但最终只有几件事x86-x64程序员必须担心:
- 首先是写线的存在(一个根本不应该担心阅读缓存!)。
- 其次,在非原子变量长度的情况下,在不同的线程中以不同的线程进行编写和阅读,这可能会导致数据撕裂,在这种情况下,您需要同步机制。
- 最后,从多个线程中的同一变量并发更新,我们为其进行了连锁操作,或者再次同步机制。)

如果您这样做:

Foo f;
// HERE: InitializeComplexClass() and "i" member init are guaranteed to be completed
passToOtherThread(&f);
/* From this point, you cannot guarantee the state/members
   of 'f' since another thread can modify it */

如果您将实例指针传递给另一个线程,则需要实现警卫,以使两个线程都与同一实例交互。如果您只打算在另一个线程上使用实例,则无需实现警卫。但是,不要像示例中那样传递堆栈指针,请通过这样的新实例:

passToOtherThread(new Foo());

并确保在完成时将其删除。

最新更新