深入理解 POSIX 线程 (pthread):从基础到高级应用
1. pthread 基础概念
1.1 什么是 pthread_t?
pthread_t
是 POSIX 线程库中用于标识线程的数据类型。每个线程都有一个唯一的 pthread_t
标识符,类似于进程 ID。
pthread_t thread_id;
关键特性:
-
不透明数据类型,具体实现因平台而异
-
可用于比较线程是否相同 (
pthread_equal()
) -
可通过
pthread_self()
获取当前线程 ID
1.2 线程创建与管理
创建线程的基本模式:
#include <pthread.h>
void* thread_function(void* arg) {
// 线程执行的代码
return NULL;
}
int main() {
pthread_t thread;
int arg = 42;
pthread_create(&thread, NULL, thread_function, &arg);
pthread_join(thread, NULL);
return 0;
}
2. 线程同步机制
2.1 互斥锁 (Mutex)
互斥锁用于保护共享资源,防止数据竞争。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void* thread_func(void* arg) { pthread_mutex_lock(&mutex); // 临界区代码 pthread_mutex_unlock(&mutex); return NULL; }
2.2 条件变量 (Condition Variables)
条件变量允许线程在某些条件不满足时挂起,直到其他线程通知条件可能已改变。
基本用法:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
bool ready = false;
// 等待线程
void* consumer(void* arg) {
pthread_mutex_lock(&mutex);
while (!ready) {
pthread_cond_wait(&cond, &mutex);
}
// 处理条件满足的情况
pthread_mutex_unlock(&mutex);
return NULL;
}
// 通知线程
void* producer(void* arg) {
pthread_mutex_lock(&mutex);
ready = true;
pthread_cond_signal(&cond); // 或 pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
return NULL;
}
Signaling for Condition Variables
条件变量的信号机制有两种:
-
pthread_cond_signal()
: 唤醒至少一个等待该条件变量的线程 -
pthread_cond_broadcast()
: 唤醒所有等待该条件变量的线程
选择原则:
-
当只有一个等待线程能被满足时,使用
signal
(更高效) -
当多个等待线程可能被满足时,使用
broadcast
2.3 何时使用 trylock?
pthread_mutex_trylock()
是非阻塞版本的互斥锁获取函数:
if (pthread_mutex_trylock(&mutex) == 0) { // 成功获取锁 pthread_mutex_unlock(&mutex); } else { // 锁已被占用,执行其他操作 }
适用场景:
-
避免死锁:当需要获取多个锁时,可以先尝试非阻塞获取
-
实现自旋锁:在短暂循环中尝试获取锁
-
非关键路径:当锁不可用时可以执行其他操作而非阻塞
3. 高级线程控制
3.1 pthread_exit
pthread_exit()
用于显式终止当前线程,并可返回一个值。
void* thread_func(void* arg) { // 线程逻辑 pthread_exit((void*)42); // 终止线程并返回值 }
关键点:
-
与
return
不同,pthread_exit()
可以用于任何函数中终止线程 -
主线程调用
pthread_exit()
会终止主线程但保持进程运行直到所有线程结束 -
返回值可通过
pthread_join()
获取
3.2 pthread_barrier
屏障用于同步多个线程,使它们在某一点等待所有线程到达后再继续执行。
pthread_barrier_t barrier;
void* thread_func(void* arg) {
// 第一阶段工作
pthread_barrier_wait(&barrier);
// 第二阶段工作(所有线程都到达屏障后执行)
return NULL;
}
int main() {
pthread_barrier_init(&barrier, NULL, 3); // 等待3个线程
// 创建3个线程...
pthread_barrier_destroy(&barrier);
return 0;
}
应用场景:
-
并行计算中的阶段同步
-
多线程初始化完成后才开始处理数据
-
测试多线程程序的确定性行为
3.3 Detached Threads
分离线程是不需要被其他线程 join 的线程,其资源在线程结束时自动回收。
void* thread_func(void* arg) {
// 线程逻辑
return NULL;
}
int main() {
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread, &attr, thread_func, NULL);
pthread_attr_destroy(&attr);
// 不需要 join
return 0;
}
特点:
-
不能被 join (
pthread_join()
会失败) -
适合"发射后不管"的任务
-
减少资源管理负担
4. 实际应用建议
-
锁的粒度:保持锁的粒度尽可能小,减少竞争
-
避免死锁:按固定顺序获取多个锁,或使用
trylock
-
条件变量检查:总是使用 while 循环检查条件,防止虚假唤醒
-
资源清理:确保线程退出时释放所有资源
-
错误检查:所有 pthread 函数调用都应检查返回值
5. 性能考虑
-
频繁的锁竞争会显著降低性能
-
读写锁 (
pthread_rwlock_t
) 在读多写少的场景更高效 -
线程局部存储 (
pthread_key_t
) 可以减少同步需求 -
线程池模式比频繁创建销毁线程更高效