线程本地指针指向非线程本地数据

时间:2012-03-28 14:08:21

标签: c linux multithreading gcc x86-64

我有一个数组,它不是线程本地的,例如以下内容。

long array[NTHREADS];

这里,数组[0]由线程0管理,数组[1]由线程1管理,依此类推。我们没有使用线程局部变量,因为在某些时候线程也必须读取其他线程的部分。但是,大多数时候他们会修改自己的部分。当然,我们可以使用array[thread_id]修改数据,但为了加快执行速度,我想使用指针。

现在,由于每个线程都管理自己的数据,因此指针应该是线程本地的并且在开头分配。所以我需要这样的东西(用gcc语法)。

  __thread long* tl_ptr;

  tl_ptr = &array[threadid];

通过这种方式,我可以使用*tl_ptr修改特定于线程的数据。现在我的问题是,如果这种方法是正确的吗?这种方法有问题吗?

2 个答案:

答案 0 :(得分:1)

C ++ 11有一个内存模型,用于定义这些情况下的行为。 C和C ++ 03及更早版本基本上是单线程的 - 没有原生的原子/栅栏。

这意味着除非您使用C ++ 11编译器(实现内存模型),否则由于缓存一致性问题等原因可能会产生奇怪的效果。这是特定于cpu的。

如果您知道要运行哪个处理器,它具有适当的“强大”内存模型,您可以确定您的方法是安全的,但它不可移植。

答案 1 :(得分:0)

如果你是用前C ++ 11方言编写程序,严格来说结果将不可移植 - 旧的C ++方言没有任何线程概念。

但是如果你使用C ++ 11(可能通过说“现在是C ++ 11”而不改变单个字节并使用编译器标志来实现)并坚持使用该语言的线程设施( thread_local代替__thread),语言可以保证可移植性。

在现实生活中,大多数pre C ++ 11的实现与线程相当好。例如gccx86_64将起作用 - gcc内置了线程机制,x86_64是一个具有强大内存模型的体系结构(在内存排序章节中阅读详细信息here)。

那就是说,你以一种奇怪的方式使用本地线程。通常的语义是

__thread long array_data;
long* array[NTHREADS];  // non thread local pointer to thread local data

现在,如果您在任何线程中修改array_data,则只会修改array_data的线程本地副本。如果你需要从另一个线程访问array_data,比如一个总结所有array_data的主管线程,你需要一个数组,它保存指向所有线程本地的指针,以及哪些元素被初始化为每个的启动线程

array[iThread] = &array_data;  // where iThread is an index for each thread

来自主管线程的访问看起来像

long sum=0;
for (int i=0; i<NTHREADS; ++i)
    sum += *array[i];

您需要确保在适当的情况下使用互斥锁序列化或保护您对来自其他线程的线程本地的访问。在你的情况下(x86和gcc),求和循环将起作用 - 对齐的longs保证你的硬件是原子的,gcc会把指针视为不受限制 - 但要小心。