mystruct** = (mystruct**)calloc(10, sizeof(mystruct*);
for(unsignd int i = 0; i < 10; i++)
mystruct[i] = (mystruct*)calloc(10, sizeof(mystruct);
thread t[10];
for(unsigned int i = 0; i < 10; i++)
t[i] = thread(new_piece, mystruct[i]);
for(unsigned int i = 0; i < 10; i++)
t[i].join();
函数new_piece
将数据写入mystruct[i]
。更具体地说,该函数会更改mystruct[i][0], mystruct[i][1], ..., mystruct[9]
如何使上述操作线程安全?
答案 0 :(得分:6)
正如评论中所提到的,代码似乎是“线程安全的”,但它可能会受到“缓存抖动”的影响。
首先让我解释它是什么,以及为什么会在你的代码中发生这种情况:
“缓存行”是从内存中提取到缓存中的最小数据单位。请注意,缓存行的大小是硬件属性。没有语言结构可以产生这个价值。现代CPU上的大多数缓存行大小都是64字节。
当不同的线程访问恰好在内存中布局共享同一缓存行的不同变量时,在具有多个CPU和一致缓存的系统上发生缓存抖动。这将导致过多的缓存未命中,从而导致性能下降。
(另请参阅wiki文章false-sharing),它引用了导致缓存抖动的使用模式)
(另见:Dr.Dobb的文章Eliminate False Sharing)
从calloc
或malloc
返回的分配块尽可能小,并且紧密排列在一起。也就是说,不同的分配块可以共享相同的缓存行(另请参阅man 3 calloc
,例如FreeBSD man page)。
C ++的new
运算符不会有所不同。
现在,我们通常不能假设从calloc
或malloc
返回的内存块不会共享公共缓存行,您的代码可能会受到缓存抖动的影响,因为您的 mystruct 可以共享一个公共缓存行。
基本上,您可以通过正确对齐数据来确保这一点(请参阅Wiki文章Data Structure Alignment)。
有许多方法可以确保您的数据( mystruct )正确缓存对齐,例如:
使用malloc
或calloc
并将分配请求四舍五入到最接近缓存行大小的倍数。
使用posix函数:posix_memalign
,在标题<stdlib.h>
中声明(请参阅opengroup posix_memalign)
在C ++ 11中,您可以使用std::aligned_storage
(参见定义here)
std::aligned_storage
提供type
,适合用作未初始化的存储空间,您可以存储对象。
例如,定义缓存行对齐的存储,它是N个实例的数组:
struct mystruct { ... };
const std::size_t cache_line_size = 64;
typename std::aligned_storage<sizeof(mystruct), cache_line_size>::type storage[N];
通过这种方式,您现在可以定义一个包含N个缓存行对齐mystruct
数组的类,它还提供了方便的访问器函数来修改和检索位置mystruct值> i 在底层数组中。 IMO,使用循环和calloc实例化mystruct
数组的存储,这种方法比你容易出错的方法更受欢迎。
请参阅示例here,稍加修改后的示例将完全符合您的需求。