我正在运行一些线程,这些线程基本上都返回相同的对象。然后,我等待所有这些操作完成,然后基本上阅读结果。为避免需要同步,我认为可以将所有结果对象预先分配在数组或向量中,并为线程提供指向每个对象的指针。在较高的层次上,代码是这样的(简化):
std::vector<Foo> results(2);
RunThread1(&results[0]);
RunThread2(&results[1]);
WaitForAll();
// Read results
cout << results[0].name << results[1].name;
基本上,我想知道此“代码”是否存在任何潜在的不安全。我想知道的一件事是,该向量是否应该声明为volatile,以使末尾的读取未得到优化并输出不正确的值。
答案 0 :(得分:3)
对该问题的简短回答是“否”,不应将数组声明为volatile
。有两个简单的原因:
不需要使用volatile
。每个理智的多线程平台都提供具有明确定义的语义的同步原语。如果使用它们,则不需要volatile
。
使用volatile
是不够的。由于volatile
在您可能使用的任何平台上都没有定义多线程语义,因此仅凭它不足以提供同步。
很可能,您在WaitForAll
中所做的任何事情都已足够。例如,如果它使用事件,互斥量,条件变量或几乎任何类似的东西,它将定义足以确保这种安全性的多线程语义。
更新:“只是出于好奇,在WaitForAll中发生的可以保证读取安全的示例是什么?它不需要有效地告诉编译器进行“刷新”吗? “是缓存还是要避免后续读取操作的优化?”
好吧,如果您使用的是pthreads,那么如果它使用pthread_join
,那将是保证读取安全性的一个示例,因为文档说该线程所做的任何事情对于连接它之后的线程都是可见的。 pthread_join
返回。
它是如何实现的,是一个实现细节。实际上,在现代系统上,没有要刷新的缓存,也没有可能但需要避免的对后续读取的任何优化。
请考虑一下,如果WaitForAll
内的某个地方存在对pthread_join
的调用。通常,您只是不让编译器查看pthread_join
的内部结构,因此编译器必须假设pthread_join
可以做其他线程可以做的任何事情。因此,将pthread_join
本身可能访问或修改该数据的信息保留为另一个线程可能在对pthread_join
的调用期间在寄存器中修改的信息。
答案 1 :(得分:1)
我想知道向量是否应该声明为volatile,以便不优化最后的读取并输出不正确的值。
不。如果存在缺少同步的问题,那么volatile将无济于事。
但是缺少同步没有问题,因为根据您的描述,您不会从多个线程访问同一对象-直到等待线程完成为止,这是同步线程的事情。
存在一个潜在的问题,如果对象很小(少于64个字节;取决于CPU体系结构),则数组中的对象共享一个缓存“行”,并且由于以下原因,对它们的访问可能会变得有效地同步:写争用。仅当线程相对于不访问输出对象的操作向变量大量写入变量时,这才是问题。
答案 2 :(得分:0)
这取决于WaitForAll()中的内容。如果同步正确,那么一切都很好。例如,互斥或线程连接将导致正确的内存同步。
不稳定将无济于事。它可能会阻止编译器优化,但不会影响CPU级别发生的任何事情,例如不更新缓存。使用适当的同步,例如互斥锁,线程连接,然后结果将是有效的(顺序一致)。不要指望银弹挥发。编译器和CPU现在非常复杂,无法保证。
其他答案将在内存栏和同步将要添加的其他说明中详细说明。:-)