6

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


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 */
}

谢谢。

4

2 回答 2

10

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

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

这肯定行不通,因为您只会修改i. 如果您希望更改保持不变,则需要传递int*or 。int&

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

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

如果您打算这样做:

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

那么就不需要i使用指针访问了。可以将i其视为对每个正在运行的线程具有不同值的全局变量。您不需要通过指针访问普通的全局变量来进行更改,因此也不需要使用带有线程局部变量的指针。

最后,线程本地存储并不是真的要为每个线程存储大量变量(TLS 表的大小存在编译器相关的限制),但这是您可以轻松解决的问题:将许多变量放入 astruct并 make指向struct线程本地的指针。

于 2011-03-27T16:56:35.347 回答
1

我看到的 TLS 的唯一问题是它可能的大小有限。这取决于系统,因此您可能会面临移植或扩展问题(顺便说一句,TLS 在某些系统上可能根本不可用)

于 2011-03-27T17:01:48.460 回答