C 语言线程局部存储:修订间差异
外观
Page creation by admin bot |
Page update by admin bot |
||
第1行: | 第1行: | ||
= C语言线程局部存储 = | |||
线程局部存储(Thread Local Storage, TLS)是C语言中一种允许每个线程拥有自己独立变量副本的机制。在多线程编程中,全局变量和静态变量默认是被所有线程共享的,而使用TLS可以创建线程私有的数据,避免竞争条件并简化线程同步。 | |||
== 基本概念 == | |||
在C11标准之前,TLS通过编译器扩展(如GCC的<code>__thread</code>或MSVC的<code>__declspec(thread)</code>)实现。C11标准正式引入了<code>_Thread_local</code>关键字(可用宏<code>thread_local</code>替代)来支持线程局部存储。 | |||
== | 线程局部存储的变量: | ||
* 每个线程拥有独立的副本 | |||
* 生命周期与线程相同 | |||
* 初始化仅在首次访问时进行(对于静态存储期变量) | |||
== 语法与声明 == | |||
声明线程局部变量的语法如下: | |||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
#include <threads.h> | #include <threads.h> | ||
thread_local int tls_var; // 文件作用域的TLS变量 | |||
void func() { | |||
static thread_local int static_tls; // 静态存储期的TLS变量 | |||
int | thread_local int local_tls; // 错误:不能声明自动存储期的TLS变量 | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== 代码示例 == | |||
以下示例展示TLS变量的基本用法: | |||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
#include <stdio.h> | #include <stdio.h> | ||
#include <threads.h> | |||
#include <stdlib.h> | |||
thread_local int counter = 0; // 每个线程有自己的counter副本 | |||
int thread_func(void *arg) { | |||
counter++; // 修改线程本地的counter | |||
printf("Thread %ld: counter = %d\n", (long)arg, counter); | |||
return 0; | |||
printf("Thread % | |||
return | |||
} | } | ||
int main() { | int main() { | ||
thrd_t t1, t2; | |||
thrd_create(&t1, thread_func, (void *)1); | |||
thrd_create(&t2, thread_func, (void *)2); | |||
thrd_join(t1, NULL); | |||
thrd_join(t2, NULL); | |||
printf("Main thread: counter = %d\n", counter); | |||
return 0; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
可能的输出: | '''可能的输出:''' | ||
<pre> | <pre> | ||
Thread | Thread 1: counter = 1 | ||
Thread | Thread 2: counter = 1 | ||
Main thread: counter = 0 | |||
</pre> | </pre> | ||
== | == 实现原理 == | ||
TLS的实现通常依赖于操作系统和编译器的协作: | |||
<mermaid> | <mermaid> | ||
graph TD | graph TD | ||
A[ | A[程序启动] --> B[为TLS变量分配存储空间] | ||
B --> C[主线程初始化] | |||
C --> D[创建新线程] | |||
D --> E[为新线程分配独立的TLS存储] | |||
E --> F[线程访问自己的TLS副本] | |||
</mermaid> | </mermaid> | ||
== 实际应用场景 == | |||
1. '''错误码存储''':如C库的<code>errno</code>通常实现为TLS变量 | |||
2. '''线程特定缓存''':避免锁竞争 | |||
3. '''随机数生成器状态''':保持每个线程的独立状态 | |||
< | |||
== 高级主题:动态TLS == | |||
除了语言级别的TLS支持,操作系统还提供API级别的TLS: | |||
* Windows: <code>TlsAlloc()</code>, <code>TlsGetValue()</code>, <code>TlsSetValue()</code> | |||
* POSIX: <code>pthread_key_create()</code>, <code>pthread_getspecific()</code>, <code>pthread_setspecific()</code> | |||
</ | |||
示例(POSIX): | |||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
#include <pthread.h> | #include <pthread.h> | ||
#include <stdio.h> | #include <stdio.h> | ||
pthread_key_t key; | |||
void destructor(void *value) { | |||
free(value); // 清理线程特定数据 | |||
} | |||
void *thread_func(void *arg) { | void *thread_func(void *arg) { | ||
int *data = malloc(sizeof(int)); | |||
*data = pthread_self(); // 使用线程ID作为示例数据 | |||
pthread_setspecific(key, data); | |||
printf("Thread %lu: data = %d\n", pthread_self(), *(int *)pthread_getspecific(key)); | |||
return NULL; | return NULL; | ||
} | } | ||
int main() { | int main() { | ||
pthread_key_create(&key, destructor); | |||
pthread_t t1, t2; | pthread_t t1, t2; | ||
第181行: | 第121行: | ||
pthread_join(t2, NULL); | pthread_join(t2, NULL); | ||
pthread_key_delete(key); | |||
return 0; | return 0; | ||
} | } | ||
第186行: | 第127行: | ||
== 性能考量 == | == 性能考量 == | ||
TLS访问通常比普通变量访问慢,因为: | |||
* 需要通过额外的内存引用(如通过线程环境块) | |||
* 某些架构需要特殊的指令支持 | |||
性能模型可以表示为: | |||
<math> | <math> | ||
T_{access} = T_{ | T_{access} = T_{base} + T_{tls\_overhead} | ||
</math> | </math> | ||
== 限制与注意事项 == | |||
1. TLS变量不能是自动存储期(不能声明在函数内非static) | |||
1. | 2. 动态库中的TLS可能有平台特定的行为 | ||
2. | 3. 大量使用TLS可能增加线程创建开销 | ||
3. | 4. 某些嵌入式平台可能不支持TLS | ||
4. | |||
== | == 总结 == | ||
线程局部存储是多线程编程中的重要工具,它: | |||
* 提供线程隔离的数据存储 | |||
* | * 避免不必要的同步开销 | ||
* | * 简化线程安全代码的编写 | ||
* | * 有语言级别和API级别两种实现方式 | ||
正确使用TLS可以显著提高多线程程序的可靠性和性能。 | |||
[[Category:编程语言]] | [[Category:编程语言]] | ||
[[Category:C]] | [[Category:C]] | ||
[[Category:C | [[Category:C 语言高级特性]] |
2025年4月29日 (二) 04:47的最新版本
C语言线程局部存储[编辑 | 编辑源代码]
线程局部存储(Thread Local Storage, TLS)是C语言中一种允许每个线程拥有自己独立变量副本的机制。在多线程编程中,全局变量和静态变量默认是被所有线程共享的,而使用TLS可以创建线程私有的数据,避免竞争条件并简化线程同步。
基本概念[编辑 | 编辑源代码]
在C11标准之前,TLS通过编译器扩展(如GCC的__thread
或MSVC的__declspec(thread)
)实现。C11标准正式引入了_Thread_local
关键字(可用宏thread_local
替代)来支持线程局部存储。
线程局部存储的变量:
- 每个线程拥有独立的副本
- 生命周期与线程相同
- 初始化仅在首次访问时进行(对于静态存储期变量)
语法与声明[编辑 | 编辑源代码]
声明线程局部变量的语法如下:
#include <threads.h>
thread_local int tls_var; // 文件作用域的TLS变量
void func() {
static thread_local int static_tls; // 静态存储期的TLS变量
thread_local int local_tls; // 错误:不能声明自动存储期的TLS变量
}
代码示例[编辑 | 编辑源代码]
以下示例展示TLS变量的基本用法:
#include <stdio.h>
#include <threads.h>
#include <stdlib.h>
thread_local int counter = 0; // 每个线程有自己的counter副本
int thread_func(void *arg) {
counter++; // 修改线程本地的counter
printf("Thread %ld: counter = %d\n", (long)arg, counter);
return 0;
}
int main() {
thrd_t t1, t2;
thrd_create(&t1, thread_func, (void *)1);
thrd_create(&t2, thread_func, (void *)2);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
printf("Main thread: counter = %d\n", counter);
return 0;
}
可能的输出:
Thread 1: counter = 1 Thread 2: counter = 1 Main thread: counter = 0
实现原理[编辑 | 编辑源代码]
TLS的实现通常依赖于操作系统和编译器的协作:
实际应用场景[编辑 | 编辑源代码]
1. 错误码存储:如C库的errno
通常实现为TLS变量
2. 线程特定缓存:避免锁竞争
3. 随机数生成器状态:保持每个线程的独立状态
高级主题:动态TLS[编辑 | 编辑源代码]
除了语言级别的TLS支持,操作系统还提供API级别的TLS:
- Windows:
TlsAlloc()
,TlsGetValue()
,TlsSetValue()
- POSIX:
pthread_key_create()
,pthread_getspecific()
,pthread_setspecific()
示例(POSIX):
#include <pthread.h>
#include <stdio.h>
pthread_key_t key;
void destructor(void *value) {
free(value); // 清理线程特定数据
}
void *thread_func(void *arg) {
int *data = malloc(sizeof(int));
*data = pthread_self(); // 使用线程ID作为示例数据
pthread_setspecific(key, data);
printf("Thread %lu: data = %d\n", pthread_self(), *(int *)pthread_getspecific(key));
return NULL;
}
int main() {
pthread_key_create(&key, destructor);
pthread_t t1, t2;
pthread_create(&t1, NULL, thread_func, NULL);
pthread_create(&t2, NULL, thread_func, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_key_delete(key);
return 0;
}
性能考量[编辑 | 编辑源代码]
TLS访问通常比普通变量访问慢,因为:
- 需要通过额外的内存引用(如通过线程环境块)
- 某些架构需要特殊的指令支持
性能模型可以表示为:
限制与注意事项[编辑 | 编辑源代码]
1. TLS变量不能是自动存储期(不能声明在函数内非static) 2. 动态库中的TLS可能有平台特定的行为 3. 大量使用TLS可能增加线程创建开销 4. 某些嵌入式平台可能不支持TLS
总结[编辑 | 编辑源代码]
线程局部存储是多线程编程中的重要工具,它:
- 提供线程隔离的数据存储
- 避免不必要的同步开销
- 简化线程安全代码的编写
- 有语言级别和API级别两种实现方式
正确使用TLS可以显著提高多线程程序的可靠性和性能。