多线程编程:线程私有与共享资源解析及线程操作函数详解
一、线程的共享资源
共享资源是指被同一进程内所有线程共同访问的数据或资源。由于多个线程可能同时操作这些资源,必须通过同步机制(如互斥锁)确保线程安全。
1.1 常见的共享资源
-
堆内存
通过malloc
或new
动态分配的内存对所有线程可见。
风险示例:int *data = malloc(sizeof(int)); *data = 42; // 线程A和线程B同时修改 data 的值
若不使用锁保护,可能导致数据竞争(Data Race)。
-
全局变量与静态变量
存储在全局数据区的变量可被所有线程访问。static int counter = 0; // 所有线程共享
-
文件描述符
打开的文件、套接字等描述符共享,需注意多线程读写时的原子性。FILE *fp = fopen("log.txt", "w"); // 多个线程同时写入需同步
-
代码段
进程的代码(函数、指令)是只读的,线程可安全并发执行。
1.2 共享资源的管理
- 互斥锁(Mutex)
确保对共享资源的独占访问。pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&lock); counter++; // 临界区操作 pthread_mutex_unlock(&lock);
二、线程的私有资源
私有资源是每个线程独立拥有的数据,其他线程无法直接访问。
2.1 常见的私有资源
-
栈内存
每个线程拥有独立的栈空间,存储局部变量和函数调用链。void thread_func() { int local_var = 10; // 栈变量,线程私有 }
-
线程局部存储(TLS)
使用__thread
(GCC)或thread_local
(C11)声明线程私有变量。__thread int tls_var; // 每个线程独立副本
-
寄存器状态
程序计数器(PC)、栈指针(SP)等寄存器由线程独立维护。
2.2 TLS的典型应用
- 避免全局变量竞争
__thread int user_id; // 每个线程独立存储用户ID
三、线程操作函数详解
POSIX线程(pthread
)库提供了丰富的线程管理接口。以下为核心函数解析。
3.1 线程创建:pthread_create
函数原型:
int pthread_create(
pthread_t *thread, // 线程ID
const pthread_attr_t *attr,// 线程属性(通常为NULL)
void *(*start_routine)(void *), // 线程入口函数
void *arg // 传递给入口函数的参数
);
示例代码:
#include <pthread.h>
#include <stdio.h>
void* print_number(void *arg) {
int num = *(int*)arg;
printf("Received: %d\n", num);
return NULL;
}
int main() {
pthread_t tid;
int value = 42;
pthread_create(&tid, NULL, print_number, &value);
pthread_join(tid, NULL);
return 0;
}
参数传递陷阱:
直接传递栈变量地址可能导致数据竞争。改进方案:
// 动态分配参数内存
int *arg = malloc(sizeof(int));
*arg = 42;
pthread_create(&tid, NULL, print_number, arg);
// 在线程函数中释放内存
3.2 线程等待:pthread_join
函数原型:
int pthread_join(pthread_t thread, void **retval);
作用:
- 阻塞调用线程,直到目标线程结束。
- 回收目标线程资源(如栈内存)。
- 获取线程返回值(通过
retval
参数)。
示例:
void* thread_func(void *arg) {
int *result = malloc(sizeof(int));
*result = 100;
return (void*)result;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
void *retval;
pthread_join(tid, &retval);
printf("Thread returned: %d\n", *(int*)retval);
free(retval); // 释放返回值内存
return 0;
}
错误处理:
ESRCH
: 无效线程ID。EINVAL
: 线程不可连接(如已分离)。EDEADLK
: 试图等待自身导致死锁。
3.3 线程终止:pthread_exit
与 pthread_self
pthread_exit
:
void pthread_exit(void *retval); // 终止当前线程并返回 retval
关键区别:
return
语句仅退出线程函数,而pthread_exit
直接终止线程。- 主线程调用
pthread_exit
后,程序会等待其他线程结束。
pthread_self
:
pthread_t pthread_self(); // 获取当前线程ID
应用场景:
- 日志记录时附加线程ID。
- 调试多线程问题。
四、线程参数传递的最佳实践
4.1 避免共享栈变量
错误示例:
for (int i = 0; i < 5; i++) {
pthread_create(&tid[i], NULL, func, &i); // 传递局部变量地址
}
// 子线程可能读取到被主线程修改后的 i
解决方案:
-
动态分配内存
int *arg = malloc(sizeof(int)); *arg = i; pthread_create(&tid[i], NULL, func, arg); // 在线程函数中 free(arg)
-
使用线程同步
通过条件变量或屏障(Barrier)确保参数传递安全。
五、线程的生命周期管理
5.1 分离线程(Detached Thread)
- 默认情况下线程是可连接的(Joinable),需手动调用
pthread_join
。 - 分离线程(Detached)结束后自动释放资源。
设置分离属性:
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, func, NULL);
pthread_attr_destroy(&attr);
六、总结与建议
- 共享资源必须同步
使用互斥锁、信号量或原子操作保护临界区。 - 优先使用线程局部存储(TLS)
替代全局变量以减少竞争。 - 谨慎传递参数
动态分配内存或复制数据避免悬垂指针。 - 及时回收资源
调用pthread_join
或设置分离属性防止资源泄漏。
附录:完整代码示例
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_THREADS 5
// 线程函数:动态分配参数内存
void* thread_func(void *arg) {
int num = *(int*)arg;
free(arg); // 释放参数内存
printf("Thread %d started\n", num);
// 模拟任务执行
int *result = malloc(sizeof(int));
*result = num * 10;
pthread_exit(result); // 返回结果
}
int main() {
pthread_t threads[NUM_THREADS];
int *results[NUM_THREADS];
// 创建线程并传递参数
for (int i = 0; i < NUM_THREADS; i++) {
int *arg = malloc(sizeof(int));
*arg = i;
if (pthread_create(&threads[i], NULL, thread_func, arg) != 0) {
perror("pthread_create failed");
exit(1);
}
}
// 等待线程结束并收集结果
for (int i = 0; i < NUM_THREADS; i++) {
void *retval;
if (pthread_join(threads[i], &retval) != 0) {
perror("pthread_join failed");
exit(1);
}
results[i] = (int*)retval;
printf("Thread %d returned %d\n", i, *results[i]);
free(results[i]); // 释放结果内存
}
return 0;
}
参考资料
- POSIX Threads Programming: https://computing.llnl.gov/tutorials/pthreads/
- 《UNIX环境高级编程》(Advanced Programming in the UNIX Environment)