线程与同步
1.线程创建与管理:pthread 库
在 C 语言中,线程的创建与管理主要通过 POSIX 线程库(pthread) 实现,核心函数包括 pthread_create(创建线程)、pthread_join(等待线程结束)、pthread_exit(线程退出),并可通过线程属性控制线程的行为(如分离态)。
1.核心函数与线程生命周期
- 线程创建:pthread_create
- 等待线程结束:pthread_join
作用:阻塞当前线程,直到目标线程结束,并回收其资源(避免 “僵尸线程”)。 - 线程退出:pthread_exit
作用:终止当前线程,并通过 retval 传递返回值(被 pthread_join 捕获)。
注意:
主线程调用 pthread_exit 会等待所有子线程结束后再退出进程;若主线程用 exit,会立即终止所有子线程。
2.线程属性:分离态(PTHREAD_CREATE_DETACHED)
线程默认是非分离态(PTHREAD_CREATE_JOINABLE),需通过 pthread_join 回收资源。若线程无需返回值且不需要被等待,可设置为分离态(PTHREAD_CREATE_DETACHED),其结束后会自动释放资源,无需 pthread_join。
设置分离态的步骤:
初始化线程属性:pthread_attr_init
设置分离态属性:pthread_attr_setdetachstate
创建线程时传入属性
销毁线程属性:pthread_attr_destroy
2.线程同步与互斥
1.互斥锁
互斥锁(Mutex)是最常用的同步工具,通过 “加锁 - 解锁” 机制保证同一时间只有一个线程访问共享资源,本质是 “二进制信号量”(状态:锁定 / 未锁定)。
- 核心函数
函数 功能 关键说明
pthread_mutex_init 初始化互斥锁 需指定属性(默认属性传 NULL)
pthread_mutex_lock 加锁(阻塞) 若锁已被占用,线程阻塞等待
pthread_mutex_trylock 加锁(非阻塞) 若锁已被占用,立即返回错误(EBUSY)
pthread_mutex_unlock 解锁 必须由持有锁的线程调用,否则行为未定义
pthread_mutex_destroy 销毁互斥锁 释放锁占用的资源,需在锁未被占用时调用
2.条件变量(pthread_cond_t):解决 “等待 - 唤醒” 问题
条件变量用于协调线程的执行顺序(如 “等待某个条件成立后再执行”),配合互斥锁使用,解决 “忙等”(线程循环检查条件,浪费 CPU 资源)问题。典型场景:生产者 - 消费者模型(生产者生产数据后唤醒消费者,消费者无数据时等待)。
- 核心函数
函数 功能 关键说明
pthread_cond_init 初始化条件变量 需指定属性(默认传 NULL)
pthread_cond_wait 等待条件成立(阻塞) 必须在互斥锁保护下调用,会自动释放锁并阻塞,被唤醒后重新获取锁
pthread_cond_signal 唤醒一个等待的线程 唤醒任意一个等待线程(通常是优先级最高的)
pthread_cond_broadcast 唤醒所有等待的线程 适用于多个线程等待同一条件的场景
pthread_cond_destroy 销毁条件变量 需在无线程等待时调用
pthread_cond_wait 强制要求传入一个已加锁的互斥锁,原因是为了避免竞态条件:
线程检查条件(如 “缓冲区是否为空”)和进入等待状态之间,可能被其他线程打断并修改条件(如另一个线程放入数据),导致 “条件已满足却错误等待”。
循环检查条件:pthread_cond_wait 可能被虚假唤醒(无信号时唤醒),需用 while 而非 if 循环检查条件;
3.线程级信号量(sem_t):轻量级同步
线程级信号量(无名信号量)是计数型同步工具,可用于线程间的互斥(计数 = 1)或资源计数(如缓冲区空位数量),比 “互斥锁 + 条件变量” 更简洁,适合简单场景。
- 核心函数(线程间使用)
函数 功能 关键说明
sem_init 初始化信号量 pshared=0 表示线程间使用,value 为初始计数
sem_wait P 操作(请求资源,阻塞) 计数 - 1,若计数 < 0 则阻塞
sem_trywait P 操作(非阻塞) 计数 < 0 时返回错误(EAGAIN)
sem_post V 操作(释放资源) 计数 + 1,若有线程阻塞则唤醒一个
sem_destroy 销毁信号量 释放资源,需在无线程等待时调用