std :: thread的线程安全数组?

时间:2014-02-11 23:04:18

标签: c++ multithreading

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]

的值

如何使上述操作线程安全?

1 个答案:

答案 0 :(得分:6)

正如评论中所提到的,代码似乎是“线程安全的”,但它可能会受到“缓存抖动”的影响。

首先让我解释它是什么,以及为什么会在你的代码中发生这种情况:

什么是缓存抖动:

“缓存行”是从内存中提取到缓存中的最小数据单位。请注意,缓存行的大小是硬件属性。没有语言结构可以产生这个价值。现代CPU上的大多数缓存行大小都是64字节。

当不同的线程访问恰好在内存中布局共享同一缓存行的不同变量时,在具有多个CPU和一致缓存的系统上发生缓存抖动。这将导致过多的缓存未命中,从而导致性能下降。

(另请参阅wiki文章false-sharing),它引用了导致缓存抖动的使用模式

(另见:Dr.Dobb的文章Eliminate False Sharing

为什么会在你的代码中发生:

callocmalloc返回的分配块尽可能小,并且紧密排列在一起。也就是说,不同的分配块可以共享相同的缓存行(另请参阅man 3 calloc,例如FreeBSD man page)。

C ++的new运算符不会有所不同。

现在,我们通常不能假设从callocmalloc返回的内存块不会共享公共缓存行,您的代码可能会受到缓存抖动的影响,因为您的 mystruct 可以共享一个公共缓存行。

如何避免“缓存抖动”:

基本上,您可以通过正确对齐数据来确保这一点(请参阅Wiki文章Data Structure Alignment)。

有许多方法可以确保您的数据( mystruct )正确缓存对齐,例如:

  • 使用malloccalloc并将分配请求四舍五入到最接近缓存行大小的倍数。

  • 使用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,稍加修改后的示例将完全符合您的需求。