线程本地存储开销

时间:2011-03-27 16:51:53

标签: c multithreading thread-local-storage

假设有一些使用全局变量的不可重入函数:


int i;
void foo(void){
/* modify i */
}

然后,我想在多线程代码中使用这个函数,所以我可以这样改变代码:


void foo(int i){
/* modify i */
}

或者,通过使用gcc __thread说明符,更简单:


__thread int i;
void foo(void){
/* modify i */
}

最后一个优点是我不需要更改另一个调用foo()的代码。

我的问题是,线程本地存储的开销是多少? TLS有一些不明显的问题吗?

如果我将通过单独的指针修改TLS`ed变量,是否有一些开销,如下所示:


__thread int i;
void foo(void){
int *p = &i;
/* modify i using p pointer */
}

感谢。

2 个答案:

答案 0 :(得分:10)

  

然后,我想使用这个功能   在多线程代码中,所以我可以改变   以这种方式编码:

void foo(int i){
    /* modify i */
}

这肯定会工作,因为您只会修改i的副本。如果您希望更改仍然存在,则需要传递int*int&

使用TLS肯定不会导致任何显着的开销(空间或时间)超过您实现相同功能可能遵循的任何自定义方法。实际上,编译器通过在保存线程局部变量的全局数据结构中动态分配存储“槽”来实现TLS。

当您在运行时访问线程局部变量时,会有一个额外的间接级别:首先,运行时必须访问当前线程的相应线程局部变量表,然后从表中获取值。这个获取是使用数组中的索引完成的(这是一个O(1)操作)。

如果您打算这样做:

__thread int i;
void foo(void){
    int *p = &i;
    /* modify i using p pointer */
}

然后不需要使用指针访问i。将i视为一个全局变量,每个正在运行的线程具有不同的值。您不需要通过指针访问正常的全局变量,因此也不需要使用带有线程局部变量的指针。

最后,线程局部存储并不是真正意味着每个线程存储大量变量(对TLS表的大小有依赖于编译器的限制)但是这是你可以轻松解决的问题:将许多变量放入a struct并指向struct线程本地。

答案 1 :(得分:1)

我在TLS看到的唯一问题是它可能有限的尺寸。这取决于系统,因此您可能面临移植或扩展问题(BTW,在某些系统上可能根本不提供TLS)