1. 线程比较
典型用法
1
2
3
4
5
6
7
8
9
10
| pthread_t main_thread = pthread_self(); // 获取主线程ID
void* worker_thread(void* arg) {
if (pthread_equal(pthread_self(), main_thread)) {
printf("This is the main thread\n");
} else {
printf("This is a worker thread\n");
}
return NULL;
}
|
2. 线程创建
1
2
| int pthread_create(pthread_t *thread, const pthread_t *attr,
void *(*start_routine)(void *), void *arg);
|
1. 执行顺序不确定性
- 新线程与主线程的执行顺序由系统调度决定,无法预先确定
- 必须通过同步机制(如互斥锁、条件变量)控制执行流程 2. 资源共享特性
- 所有线程共享进程的全局变量和堆内存
- 需要特别注意线程安全问题,避免数据竞争
- 示例:多个线程同时修改全局变量需加锁保护 3. 属性继承规则
- 继承调用线程的浮点运算环境
- 继承信号屏蔽字(Signal Mask)
- 不继承挂起信号(Pending Signals)
- 资源管理要求
- 必须显式回收线程资源(pthread_join)
- 或设置为分离线程(pthread_detach)
- 避免线程资源泄漏
3. 线程终止
线程退出的三种方式
1. 从启动例程返回
实现方式:线程函数执行到return语句正常退出
1
2
3
4
| void* thread_func(void* arg) {
// ...线程逻辑...
return (void*)42; // 返回退出码
}
|
2. 被其他线程取消
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| /*
调用线程:pthread_cancel(thread_id)
目标线程需设置可取消状态(默认启用)
异步终止可能引发资源泄漏
应在取消点(如pthread_testcancel())安全退出
*/
// 线程A取消线程B
pthread_cancel(thread_b);
// 线程B的可取消逻辑
void* thread_func(void* arg) {
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
while(1) {
pthread_testcancel(); // 安全检查点
// ...工作逻辑...
}
}
|
3. 调用pthread_exit
1
2
3
4
5
6
7
8
9
10
11
12
| /*
可在线程任意位置退出
退出码可由pthread_join获取
主线程调用时不会终止进程(与exit区别)
*/
void* thread_func(void* arg) {
if(error_occurred) {
pthread_exit((void*)-1); // 异常退出
}
// ...正常逻辑...
}
|
常见api
每个 pthread_create 应对应 pthread_join 或 pthread_detach,避免僵尸线程。
pthread_exit
1
2
3
4
5
6
7
8
9
10
11
12
| // 1. 显式终止当前线程,并返回一个退出值(retval)。
pthread_exit(void *retval)
// 2. 仅终止调用线程,不影响其他线程
// 3. retval 可被其他线程通过 pthread_join 获取
// 4. 与 exit() 的区别:
pthread_exit(NULL); // 仅终止当前线程
exit(0); // 终止整个进程(包括所有线程)
// 5. 若主线程调用 pthread_exit,进程会保持运行直到所有线程结束。
|
pthread_join
1
2
3
4
5
6
| // 1. 阻塞等待目标线程结束,并获取其退出值。
pthread_join(pthread_t thread, void **retval)
void *exit_code;
pthread_join(thread_id, &exit_code);
printf("线程退出码:%ld\n", (long)exit_code);
|
pthread_cancel
略
线程清理处理程序(Thread Cleanup Handlers)
pthread_cleanup_push/pthread_cleanup_pop 提供线程资源清理的栈式管理,类似C++的RAII机制,确保:
1
2
3
| pthread_cleanup_push(rtn, arg); // 注册清理函数
/* 临界区代码 */
pthread_cleanup_pop(execute); // 注销清理函数
|
触发的三种方式:
- 线程被取消时(pthread_cancel)
- 线程主动调用pthread_exit
- 执行pthread_cleanup_pop(非零参数)
请注意, 调用顺序与注册顺序相反
1
2
3
4
5
6
7
8
9
10
11
12
| // 举例说明
void* thread_func(void* arg) {
char *buf = malloc(1024); // 危险资源
pthread_cleanup_push(free, buf); // 注册保险
// 使用buf(可能出错或被取消)
if (error) pthread_exit(NULL);
pthread_cleanup_pop(1); // 正常退出也free 非0 执行清理函数并移除
return NULL;
}
|
pthread_detach
1
2
3
| // 1. 将指定线程标记为分离状态(detached state),使该线程终止时系统自动回收其资源,无需其他线程调用pthread_join。
// 2. 不可用 pthread_join 回收已经分离的线程, 会产生未定义行为
int pthread_detach(pthread_t thread);
|
4. 线程同步
互斥量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #include <pthread.h>
// 初始化
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 超时自动释放
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
|
避免死锁
- 顺序锁
- try_lock, 未获取锁则释放自身持有的锁
- 超时机制 死锁检测等
读写锁
1
2
3
4
| xm@hcss-ecs-4208:/tmp/tmp.17PXDZ8IS3$ man pthread_rwlock
pthread_rwlock_destroy pthread_rwlock_wrlock
pthread_rwlock_rdlock pthread_rwlock_unlock
pthread_rwlock_timedrdlock pthread_rwlock_timedwrlock
|
条件变量
1
2
3
4
5
| // 常见api
pthread_cond_init
pthread_cond_wait
pthread_cond_signal
pthread_cond_destroy
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // 举例说明
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int shared_data = 0; // 共享数据
// 等待线程(消费者)
void* consumer(void* arg) {
pthread_mutex_lock(&mutex);
while (shared_data == 0) { // 必须用 while(避免虚假唤醒)
pthread_cond_wait(&cond, &mutex); // 先释放锁,再阻塞线程
// 被唤醒后,会 重新获取锁
}
printf("Consumed: %d\n", shared_data);
pthread_mutex_unlock(&mutex);
return NULL;
}
// 通知线程(生产者)
void* producer(void* arg) {
pthread_mutex_lock(&mutex);
shared_data = 42; // 修改共享数据
pthread_cond_signal(&cond); // 唤醒一个等待线程
pthread_mutex_unlock(&mutex);
return NULL;
}
|
信号量
自旋锁
1
2
3
4
5
6
7
8
| pthread_spin_init() 初始化自旋锁
pthread_spin_destroy() 销毁自旋锁
pthread_spin_lock() 阻塞式获取锁(自旋等待)
pthread_spin_trylock() 非阻塞式尝试获取锁
pthread_spin_unlock() 释放锁
// 0 线程间共享 1 进程间共享
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
|
** 为什么要用自旋锁, 不直接用pthread_mutex ?**
(1)避免上下文切换的开销 (2)适用于极短临界区 (3)多核 CPU 的优势
5. 屏障
1
| 屏障(Barrier) 是一种同步机制,用于确保多个线程(或进程)在某个点上全部到达后才能继续执行。
|
1
2
3
| pthread_barrier_init() 初始化屏障
pthread_barrier_wait() 线程等待屏障
pthread_barrier_destroy() 销毁屏障
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| // 举例说明
#include <stdio.h>
#include <pthread.h>
#define THREAD_NUM 4
pthread_barrier_t barrier;
void* task(void* arg) {
int id = *(int*)arg;
printf("Thread %d: Phase 1\n", id);
// 模拟计算
sleep(1);
// 等待所有线程完成 Phase 1
pthread_barrier_wait(&barrier);
// 只有所有线程到达后,才会执行 Phase 2
printf("Thread %d: Phase 2\n", id);
return NULL;
}
int main() {
pthread_t threads[THREAD_NUM];
int ids[THREAD_NUM];
// 初始化屏障(等待 4 个线程)
pthread_barrier_init(&barrier, NULL, THREAD_NUM);
// 创建 4 个线程
for (int i = 0; i < THREAD_NUM; i++) {
ids[i] = i;
pthread_create(&threads[i], NULL, task, &ids[i]);
}
// 等待线程结束
for (int i = 0; i < THREAD_NUM; i++) {
pthread_join(threads[i], NULL);
}
// 销毁屏障
pthread_barrier_destroy(&barrier);
return 0;
}
|