Jam*_*ton 11 c c++ multithreading thread-safety
假设我有一个程序初始化一个全局变量供线程使用,如下所示:
int ThreadParameter;
// this function runs from the main thread
void SomeFunction() {
ThreadParameter = 5;
StartThread(); // some function to start a thread
// at this point, ThreadParameter is NEVER modified.
}
// this function is run in a background worker thread created by StartThread();
void WorkerThread() {
PrintValue(ThreadParameter); // we expect this to print "5"
}
Run Code Online (Sandbox Code Playgroud)
这些问题应适用于可能遇到的任何通用处理器体系结构.我希望解决方案是可移植的 - 不是特定于具有更强内存保证的架构,如x86.
volatile; 是否可能会在StartThread()通话结束后重新订购并留下我的话?如何解决这个问题?ThreadParameter程序开始运行之前,包含的内存块已被分页到每个处理器的缓存中SomeFunction(). SomeFunction()写5来ThreadParameter,它获取存储在第一处理器的缓存,然后开始工作线程,它运行在第二处理器上.WorkerThread()由于第二个处理器中的内存页面还没有看到第一个处理器的更新,否则第二个处理器上是否会看到未初始化的数据ThreadParameter而不是预期的值5?int,我可以使用指向更复杂的数据类型的指针,这些数据类型不一定在多线程环境中使用?如果我的担忧没有根据,那么我不需要担心的具体原因是什么?
从您的描述来看,您似乎正在启动任何子线程之前写入 ThreadParameter (或其他一些数据结构),并且您将永远不会再次写入 ThreadParameter ...它的存在是为了根据需要读取,但在其之后再也不会更改初始化; 那是对的吗?如果是这样,那么每次子线程想要读取数据时,甚至是第一次读取数据时,都不需要使用任何线程同步系统调用(或处理器/编译器原语)。
易失性的处理在某种程度上是特定于编译器的;我知道至少在 Diab for PowerPC 中,有一个关于处理易失性的编译器选项:要么在每次读/写变量后使用 PowerPC EIEIO(或 MBAR)指令,要么不使用它...除了禁止与变量关联的编译器优化之外。(EIEIO/MBAR 是PowerPC 的指令,用于禁止处理器本身对I/O 进行重新排序;即该指令之前的所有I/O 必须在该指令之后的任何I/O 之前完成)。
从正确性/安全性的角度来看,将其声明为易失性并没有什么坏处。但从务实的角度来看,如果您在 StartThread() 之前初始化 ThreadParameter 足够远,则实际上没有必要将其声明为 挥发性的(并且不这样做会加快它的所有后续访问速度)。几乎任何实质性的函数调用(例如,也许对 printf() 或 cout,或任何系统调用等)都会发出比必要的指令多几个数量级的指令,以确保处理器不可能很久以前就不会处理写入调用 StartThread() 之前的 ThreadParameter。实际上,StartThread() 本身几乎肯定会在相关线程实际启动之前执行足够的指令。因此,我建议您实际上并不需要将其声明为易失性,即使您在调用 StartThread() 之前立即初始化它也可能不需要。
现在,关于您的问题,如果在运行主线程的处理器执行初始化之前,包含该变量的页面已经加载到两个处理器的缓存中会发生什么:如果您使用的是具有类似类型的常用通用平台CPU、硬件应该已经就位来为您处理缓存一致性。在通用平台上,无论是否是多处理器,您遇到缓存一致性问题的地方是当您的处理器具有单独的指令和数据缓存并且您编写自修改代码时:写入内存的指令与数据无法区分,因此 CPU 不会使指令缓存中的这些位置无效,因此指令缓存中可能存在过时的指令,除非您随后使指令缓存中的这些位置无效(发出您自己的特定于处理器的汇编指令,您可能不会这样做)允许执行的操作取决于您的操作系统和线程的权限级别,或者为您的操作系统发出适当的缓存无效系统调用)。但您所描述的不是自修改代码,因此在这方面您应该是安全的。
您的问题 1 询问如何在所有处理器架构中确保这一点的安全。好吧,正如我上面所讨论的,如果您使用数据总线正确桥接的同类处理器,您应该是安全的。为多处理器互连设计的通用处理器具有总线监听协议来检测对共享内存的写入...只要您的线程库正确配置了共享内存区域。如果您在嵌入式系统中工作,您可能必须在 BSP 中自行配置...对于 PowerPC,您需要查看 MMU/BAT 配置中的 WIMG 位;我不熟悉其他架构,无法为您提供有关这些架构的指导。但是......如果您的系统是自制的或者如果您的处理器不是同类的,您可能无法指望两个处理器能够窥探彼此的写入;请咨询您的硬件人员以获取建议。