让我们假设您有一个多线程运行程序。在我的情况下,程序使用std :: thread,但这并不重要。现在每个线程都需要写入一些全局缓冲区,但保证它们永远不会写入此缓冲区中的相同内存地址。问题:
上面的代码只是我所说的“线程写入同一个缓冲区但从不在同一个地址”的例子(尽管缓冲区在我的情况下要大得多,并且线程访问该缓冲区的非常不同的部分)
int *buffer = new buffer[10];
for (int i = 0; i < 10; ++i) {
threads[k] = std::thread(threadFunc, std::ref(buffer), i);
}
void threadFunc(int *&buffer, const int &threadId)
{
buffer[threadId] = threadId;
}
答案 0 :(得分:3)
如果两个线程在没有同步的情况下执行冲突访问,则多线程访问会创建未定义的行为(竞争条件或更糟)。
对相同内存位置的两次访问冲突。访问单独的数组元素是安全的。
在标准中,1.7p3表示
存储器位置是标量类型的对象或者具有非零宽度的相邻位域的最大序列。 [注意:该语言的各种功能,例如引用和虚函数,可能涉及程序无法访问但由实现管理的其他内存位置。 - 结束注释]两个或多个执行线程(1.10)可以更新和访问单独的内存位置,而不会相互干扰。
然而,它可能不会有效率。如果多个数组元素适合单个缓存行,则线程将争夺所有权。这称为"false sharing",基本上否定了首先使用缓存的性能优势。为了克服这个问题,可能需要添加填充,以便不同的缓存行中存在不同的数组元素。
答案 1 :(得分:0)
...但保证永远不会写入相同的内存地址 这个缓冲区
这使得线程安全。读取效率很高,但写入可能会在某些体系结构上产生缓存争用。
然而,缓冲区必须相当大,因为线程ID通常是数百个。你不能在没有同步的情况下使用类似地图的策略,因此由于稀疏性,你必须使用稀疏数组,如buffer[12]
和buffer[134]
等。除非你是为每个线程分配一个你自己的ID,这需要在每个线程中都有状态......这个想法被我在下面的内容所取代。
最好的策略通常是让每个线程的堆栈保存一个指向静态内存位置的指针,并将此上下文作为参数传递给需要它的线程中的各种函数。这就是上下文对象在多线程编程中非常有用的原因。