pthread库函数解析

POSIX线程库 (pthread.h)

pthread.h 是 POSIX 线程(POSIX threads)接口的头文件,提供了线程创建、管理及同步原语(互斥量、条件变量、读写锁、自旋锁、屏障等)的操作函数。编译时需链接 -lpthread-pthread

线程管理

c 复制代码
// 创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void *), void *arg);
thread:指向 pthread_t 的指针,用于存储新线程的标识符
attr:线程属性对象,NULL 表示使用默认属性
start_routine:线程入口函数,签名为 void* func(void*)
arg:传递给入口函数的参数,不需要传参时设为 NULL
返回值:成功返回 0,失败返回错误编号(非 errno,需直接使用返回值)

// 等待线程结束(阻塞)
int pthread_join(pthread_t thread, void **retval);
thread:目标线程标识符
retval:指向指针的指针,用于接收线程返回值
    若线程被取消,则值为 PTHREAD_CANCELED
    不关心返回值时设为 NULL
返回值:成功返回 0,失败返回错误编号
注意:目标线程必须是 joinable 状态(非 detached),且只能被 join 一次

// 分离线程
int pthread_detach(pthread_t thread);
thread:目标线程标识符
返回值:成功返回 0,失败返回错误编号
说明:将线程设为 detached 状态,线程结束后资源自动回收,不可再被 pthread_join
      也可在创建时通过属性设置 PTHREAD_CREATE_DETACHED

// 终止当前线程
void pthread_exit(void *retval);
retval:线程的返回值,可被 pthread_join 获取
说明:线程函数 return 等价于调用 pthread_exit
      主线程调用 pthread_exit 后,进程不会退出,直到所有线程终止

// 获取当前线程标识符
pthread_t pthread_self(void);
返回值:当前线程的 pthread_t 标识符

// 比较两个线程标识符是否相同
int pthread_equal(pthread_t t1, pthread_t t2);
返回值:相等返回非零值,不等返回 0

// 取消线程
int pthread_cancel(pthread_t thread);
thread:目标线程标识符
返回值:成功返回 0,失败返回错误编号
说明:向目标线程发送取消请求,线程是否响应取决于其取消状态和类型
      取消状态:PTHREAD_CANCEL_ENABLE(默认)/ PTHREAD_CANCEL_DISABLE
      取消类型:PTHREAD_CANCEL_DEFERRED(默认,延迟到取消点)/ PTHREAD_CANCEL_ASYNCHRONOUS(立即取消)

互斥量 (Mutex)

c 复制代码
// 初始化互斥量
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
mutex:指向互斥量对象的指针
attr:互斥量属性,NULL 表示使用默认属性
返回值:成功返回 0,失败返回错误编号
说明:也可使用静态初始化 PTHREAD_MUTEX_INITIALIZER(仅适用于默认属性)

// 加锁(阻塞)
int pthread_mutex_lock(pthread_mutex_t *mutex);
mutex:指向互斥量对象的指针
返回值:成功返回 0,失败返回错误编号
说明:若互斥量已被其他线程持有,调用线程将阻塞直到获得锁

// 尝试加锁(非阻塞)
int pthread_mutex_trylock(pthread_mutex_t *mutex);
mutex:指向互斥量对象的指针
返回值:成功返回 0,失败返回错误编号(EBUSY 表示锁已被占用)
说明:若互斥量已被锁定,立即返回 EBUSY 而不阻塞

// 带超时加锁
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout);
mutex:指向互斥量对象的指针
abs_timeout:绝对超时时间(基于 CLOCK_REALTIME)
返回值:成功返回 0,超时返回 ETIMEDOUT,其他失败返回错误编号

// 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
mutex:指向互斥量对象的指针
返回值:成功返回 0,失败返回错误编号
说明:解锁者不一定是加锁者,但通常建议同一线程加锁/解锁

// 销毁互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);
mutex:指向互斥量对象的指针
返回值:成功返回 0,失败返回错误编号
说明:销毁前确保互斥量未被锁定,销毁后不可再使用

条件变量 (Condition Variable)

c 复制代码
// 初始化条件变量
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
cond:指向条件变量对象的指针
attr:条件变量属性,NULL 表示使用默认属性
返回值:成功返回 0,失败返回错误编号
说明:也可使用静态初始化 PTHREAD_COND_INITIALIZER

// 等待条件变量(阻塞)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
cond:指向条件变量对象的指针
mutex:指向已加锁的互斥量对象的指针
返回值:成功返回 0,失败返回错误编号
说明:调用前必须先对 mutex 加锁
      函数内部会原子性地释放 mutex 并阻塞等待
      被唤醒返回时,会重新获取 mutex(可能需要再次阻塞)

// 带超时等待条件变量
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
                            const struct timespec *abs_timeout);
cond:指向条件变量对象的指针
mutex:指向已加锁的互斥量对象的指针
abs_timeout:绝对超时时间(基于 CLOCK_REALTIME)
返回值:成功返回 0,超时返回 ETIMEDOUT,其他失败返回错误编号

// 唤醒一个等待线程
int pthread_cond_signal(pthread_cond_t *cond);
cond:指向条件变量对象的指针
返回值:成功返回 0,失败返回错误编号
说明:唤醒至少一个等待 cond 的线程(若有多个等待者,具体唤醒哪个不确定)

// 唤醒所有等待线程
int pthread_cond_broadcast(pthread_cond_t *cond);
cond:指向条件变量对象的指针
返回值:成功返回 0,失败返回错误编号
说明:唤醒所有等待 cond 的线程,适用于状态变化影响多个线程的场景

// 销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
cond:指向条件变量对象的指针
返回值:成功返回 0,失败返回错误编号
说明:销毁前确保没有线程在等待该条件变量

条件变量与信号量使用上的区别

  1. 条件变量是瞬时通知,信号量是维护一个值
    条件变量:如果先执行了唤醒(signal),而后执行等待(wait),这次唤醒会彻底丢失,后等待的线程会永远卡死(除非后续再有新的唤醒)。
    信号量:自带记忆和计数功能,每 post 一次就累加一次,即使没有线程在等待,这个 +1 也会被保存下来,供后续 wait 使用。
  2. 条件变量必须与互斥量配合使用,信号量可以独立使用。
    条件变量:pthread_cond_wait 必须配合 pthread_mutex_lock 使用
    信号量:sem_wait 和 sem_post 不需要外部加锁,内部自带原子性。
    为什么使用原子操作就不用加锁?
    链接:
    https://blog.csdn.net/wxr2268715051/article/details/163923932?spm=1011.2415.3001.10575\&sharefrom=mp_manage_link
  3. 虚假唤醒
    条件变量:pthread_cond_wait 可能被虚假唤醒(返回但条件不成立),因此必须用 while(条件不成立) 包裹,这是强制性的范式。
    信号量:sem_wait 只要返回,计数器就一定减 1 了,不存在虚假唤醒

读写锁 (Read-Write Lock)

c 复制代码
// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
rwlock:指向读写锁对象的指针
attr:读写锁属性,NULL 表示使用默认属性
返回值:成功返回 0,失败返回错误编号
说明:也可使用静态初始化 PTHREAD_RWLOCK_INITIALIZER

// 获取读锁(阻塞)
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
rwlock:指向读写锁对象的指针
返回值:成功返回 0,失败返回错误编号
说明:多个线程可同时持有读锁,但写锁被持有时读锁请求会阻塞

// 尝试获取读锁(非阻塞)
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
rwlock:指向读写锁对象的指针
返回值:成功返回 0,失败返回错误编号(EBUSY 表示锁已被占用)

// 带超时获取读锁
int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, const struct timespec *abs_timeout);
rwlock:指向读写锁对象的指针
abs_timeout:绝对超时时间
返回值:成功返回 0,超时返回 ETIMEDOUT,其他失败返回错误编号

// 获取写锁(阻塞)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
rwlock:指向读写锁对象的指针
返回值:成功返回 0,失败返回错误编号
说明:写锁是独占的,任何读锁或写锁被持有时,写锁请求都会阻塞

// 尝试获取写锁(非阻塞)
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
rwlock:指向读写锁对象的指针
返回值:成功返回 0,失败返回错误编号(EBUSY 表示锁已被占用)

// 带超时获取写锁
int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *abs_timeout);
rwlock:指向读写锁对象的指针
abs_timeout:绝对超时时间
返回值:成功返回 0,超时返回 ETIMEDOUT,其他失败返回错误编号

// 释放读写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
rwlock:指向读写锁对象的指针
返回值:成功返回 0,失败返回错误编号
说明:释放当前线程持有的读锁或写锁

// 销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
rwlock:指向读写锁对象的指针
返回值:成功返回 0,失败返回错误编号
说明:销毁前确保读写锁未被任何线程持有

自旋锁 (Spin Lock)

c 复制代码
// 初始化自旋锁
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
lock:指向自旋锁对象的指针
pshared:进程共享属性
    PTHREAD_PROCESS_PRIVATE:仅同一进程内线程可用(默认)
    PTHREAD_PROCESS_SHARED:可跨进程使用(需位于共享内存中)
返回值:成功返回 0,失败返回错误编号

// 获取自旋锁(忙等待/自旋)
int pthread_spin_lock(pthread_spinlock_t *lock);
lock:指向自旋锁对象的指针
返回值:成功返回 0,失败返回错误编号
说明:若锁已被占用,线程会在循环中反复检查(忙等待),不会让出 CPU
      适用于临界区极短、持锁时间极少的场景

// 尝试获取自旋锁(非阻塞)
int pthread_spin_trylock(pthread_spinlock_t *lock);
lock:指向自旋锁对象的指针
返回值:成功返回 0,失败返回错误编号(EBUSY 表示锁已被占用)

// 释放自旋锁
int pthread_spin_unlock(pthread_spinlock_t *lock);
lock:指向自旋锁对象的指针
返回值:成功返回 0,失败返回错误编号

// 销毁自旋锁
int pthread_spin_destroy(pthread_spinlock_t *lock);
lock:指向自旋锁对象的指针
返回值:成功返回 0,失败返回错误编号

屏障 (Barrier)

c 复制代码
// 初始化屏障
int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr,
                          unsigned count);
barrier:指向屏障对象的指针
attr:屏障属性,NULL 表示使用默认属性
count:必须到达屏障的线程数量
返回值:成功返回 0,失败返回错误编号

// 等待屏障
int pthread_barrier_wait(pthread_barrier_t *barrier);
barrier:指向屏障对象的指针
返回值:成功返回 0 或 PTHREAD_BARRIER_SERIAL_THREAD(给最后一个到达的线程)
        失败返回错误编号
说明:调用线程阻塞,直到指定数量(count)的线程都调用了此函数
      所有线程到达后屏障打开,所有线程被唤醒继续执行
      屏障可重复使用:所有线程离开后屏障自动重置

// 销毁屏障
int pthread_barrier_destroy(pthread_barrier_t *barrier);
barrier:指向屏障对象的指针
返回值:成功返回 0,失败返回错误编号
说明:销毁前确保没有线程在屏障上等待

线程特定数据 (Thread-Specific Data / TLS)

c 复制代码
// 创建键
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
key:指向 pthread_key_t 的指针,用于存储创建的键
destructor:析构函数指针,线程退出时对非 NULL 的线程特定数据调用
    不需要析构时设为 NULL
返回值:成功返回 0,失败返回错误编号
说明:键被所有线程共享,但每个线程通过键关联的数据是独立的

// 设置线程特定数据
int pthread_setspecific(pthread_key_t key, const void *value);
key:由 pthread_key_create 创建的键
value:与键关联的值(通常为动态分配内存的指针)
返回值:成功返回 0,失败返回错误编号

// 获取线程特定数据
void *pthread_getspecific(pthread_key_t key);
key:由 pthread_key_create 创建的键
返回值:与当前线程和 key 关联的值,未设置时返回 NULL

// 删除键
int pthread_key_delete(pthread_key_t key);
key:要删除的键
返回值:成功返回 0,失败返回错误编号
说明:删除键不会触发析构函数,也不释放各线程关联的数据内存
相关推荐
Escalating_xu1 小时前
【Linux线程同步】从数据竞争到 mutex、条件变量与生产者消费者(上篇)
android·java·linux
晨曦花锦1 小时前
Vim基础操作与Ollama模型管理,顺便配置Nginx反向代理
linux·nginx·编辑器·vim
Eloudy1 小时前
Ubuntu 22.04 从源码编译安装 ROS 2 Jazzy 笔记
linux·笔记·ubuntu
raindayinrain2 小时前
深入理解Linux内核-页表,TLB,高速缓存,性能优化
linux·性能优化·高速缓存·页表·tlb
秣宇2 小时前
银河麒麟服务器操作系统关闭 Swap 分区
linux·运维·服务器·github·kylin
RisunJan2 小时前
Linux命令-usernetctl(已废弃 - 通过 usermode-helper 控制网络接口的包装器)
linux·运维·服务器
码农爱学习3 小时前
ClaudeCode搭配DeepSeek在Windows和Linux中的安装教程
linux·运维·windows
阿昭L3 小时前
Linux文件IO
linux
小此方3 小时前
Linux加餐(一):藏在Linux中的设计模式(一)策略模式与日志
linux·设计模式·策略模式