一文打尽pthread库
pthread(POSIX Threads)是遵循 POSIX 标准的线程库,广泛用于 Unix/Linux 系统中实现多线程编程。它提供了一组 C 语言 API,用于创建、管理、同步线程。以下是对 pthread 库及相关函数的系统性总结:
一、基本概念
- 线程(Thread):轻量级进程,共享进程地址空间,独立执行流。
- 主线程:程序启动时默认创建的线程。
- 并发 vs 并行:并发是逻辑上同时执行,并行是物理上同时执行(多核)。
- 线程安全:函数/数据结构在多线程环境下能正确工作。
二、核心数据类型
#include <pthread.h>
pthread_t // 线程标识符(类似进程的 pid)
pthread_attr_t // 线程属性结构体
pthread_mutex_t // 互斥锁
pthread_cond_t // 条件变量
pthread_rwlock_t // 读写锁
三、线程管理函数
1. 创建线程
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void*),
void *arg);
thread
:输出参数,新线程 ID。attr
:线程属性,NULL 表示默认。start_routine
:线程入口函数。arg
:传递给入口函数的参数。- 返回值:0 成功,非 0 错误码(非 errno)。
⚠️ 注意:必须链接
-lpthread
(或-pthread
)
2. 等待线程结束(阻塞)
int pthread_join(pthread_t thread, void **retval);
- 阻塞调用线程,直到目标线程结束。
- 可获取线程返回值(通过
retval
)。 - 适用于需要同步或回收资源的场景。
3. 分离线程(非阻塞回收)
int pthread_detach(pthread_t thread);
- 线程结束后自动释放资源,无需
pthread_join
。 - 不能对已分离线程调用
pthread_join
。
4. 获取当前线程 ID
pthread_t pthread_self(void);
5. 比较线程 ID
int pthread_equal(pthread_t t1, pthread_t t2);
- 返回非 0 表示相等。
6. 退出线程
void pthread_exit(void *retval);
- 主动终止当前线程,可传递返回值。
- 主线程调用
pthread_exit
不会终止整个进程(除非是最后一个线程)。
四、线程属性设置(可选)
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
// 设置分离状态
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
// detachstate: PTHREAD_CREATE_JOINABLE / PTHREAD_CREATE_DETACHED
// 设置栈大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
五、线程同步机制
1. 互斥锁(Mutex)
用于保护临界区,防止多个线程同时访问共享资源。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *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_destroy(pthread_mutex_t *mutex);
🔒 使用模式:
pthread_mutex_lock(&mutex); // 临界区代码 pthread_mutex_unlock(&mutex);
2. 条件变量(Condition Variable)
用于线程间通信,常与互斥锁配合使用,实现“等待某个条件成立”。
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); // 阻塞等待
int pthread_cond_timedwait(...); // 带超时
int pthread_cond_signal(pthread_cond_t *cond); // 唤醒一个等待线程
int pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒所有等待线程
int pthread_cond_destroy(pthread_cond_t *cond);
🔄 典型使用模式:
pthread_mutex_lock(&mutex); while (condition_is_false) { pthread_cond_wait(&cond, &mutex); // 自动释放锁,被唤醒后重新加锁 } // 执行操作 pthread_mutex_unlock(&mutex);
3. 读写锁(Read-Write Lock)
允许多个读者同时访问,写者独占访问。
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
int pthread_rwlock_init(...);
int pthread_rwlock_rdlock(...); // 读锁
int pthread_rwlock_wrlock(...); // 写锁
int pthread_rwlock_unlock(...);
int pthread_rwlock_destroy(...);
适合“读多写少”场景。
4. 自旋锁(Spinlock)【可选】
忙等待锁,适用于锁持有时间极短的场景。
pthread_spinlock_t spinlock;
int pthread_spin_init(...);
int pthread_spin_lock(...);
int pthread_spin_unlock(...);
int pthread_spin_destroy(...);
六、线程局部存储(TLS)
每个线程拥有独立的变量副本。
pthread_key_t key;
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_setspecific(pthread_key_t key, const void *value);
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);
类似于 C11 的
_Thread_local
或 GCC 的__thread
。
七、取消机制(Cancellation)
允许一个线程请求终止另一个线程。
int pthread_cancel(pthread_t thread); // 发送取消请求
// 设置取消状态和类型
int pthread_setcancelstate(int state, int *oldstate);
// state: PTHREAD_CANCEL_ENABLE / PTHREAD_CANCEL_DISABLE
int pthread_setcanceltype(int type, int *oldtype);
// type: PTHREAD_CANCEL_DEFERRED(默认,到取消点) / PTHREAD_CANCEL_ASYNCHRONOUS
void pthread_testcancel(void); // 显式设置取消点
⚠️ 取消点(Cancellation Points):如
sleep
,read
,write
,pthread_cond_wait
等阻塞函数。
八、一次初始化(One-time Initialization)
确保某段初始化代码只执行一次。
pthread_once_t once_control = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
常用于初始化全局资源(如互斥锁、TLS key)。
九、常见错误处理
pthread 函数不设置 errno,而是直接返回错误码:
int ret = pthread_create(...);
if (ret != 0) {
fprintf(stderr, "Error: %s\n", strerror(ret));
}
或使用 perror
需要先设置 errno:
errno = ret;
perror("pthread_create");
十、编译与链接
gcc -o program program.c -lpthread
# 或推荐使用:
gcc -o program program.c -pthread # 自动定义宏、链接库
十一、最佳实践与注意事项
- 避免死锁:加锁顺序一致,避免嵌套锁。
- 资源回收:join 或 detach 所有线程,避免资源泄漏。
- 线程安全函数:避免使用非线程安全函数(如
strtok
,rand
→ 改用strtok_r
,rand_r
)。 - 信号处理:线程中慎用信号,推荐使用
sigwait
或指定信号处理线程。 - 性能考虑:锁粒度不宜过大,避免频繁加锁。
- 调试工具:使用
valgrind --tool=helgrind
或ThreadSanitizer
检测竞争条件。
十二、简单示例
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* thread_func(void* arg) {
int id = *(int*)arg;
printf("Thread %d running\n", id);
sleep(1);
pthread_exit((void*)(long)id);
}
int main() {
pthread_t t1, t2;
int id1 = 1, id2 = 2;
pthread_create(&t1, NULL, thread_func, &id1);
pthread_create(&t2, NULL, thread_func, &id2);
void* retval;
pthread_join(t1, &retval);
printf("Thread 1 returned: %ld\n", (long)retval);
pthread_join(t2, &retval);
printf("Thread 2 returned: %ld\n", (long)retval);
return 0;
}
总结
功能 | 主要函数 |
---|---|
创建线程 | pthread_create |
等待线程 | pthread_join |
分离线程 | pthread_detach |
互斥锁 | pthread_mutex_* |
条件变量 | pthread_cond_* |
读写锁 | pthread_rwlock_* |
线程局部存储 | pthread_key_* |
一次初始化 | pthread_once |
线程取消 | pthread_cancel , pthread_testcancel |
掌握 pthread 库是进行 Linux/Unix 系统级并发编程的基础。合理使用同步机制、避免竞态条件和死锁,是编写健壮多线程程序的关键。
✅ 推荐学习顺序:线程创建 → 互斥锁 → 条件变量 → 读写锁 → 高级特性(TLS、取消、一次初始化)
📚 参考资料:《UNIX环境高级编程》、《POSIX标准文档》、Linux man pages (man pthread_create
)
以下是《UNIX环境高级编程》和《POSIX标准文档》的官方或权威获取链接:
📘 1.《UNIX环境高级编程》(Advanced Programming in the UNIX Environment, 简称 APUE)
- 作者:W. Richard Stevens & Stephen A. Rago
- 当前最新版:第3版(2013),涵盖 POSIX.1-2008 和 UNIX System V / BSD 扩展
- 官方出版商页面:
👉 https://www.apuebook.com/
该网站由作者 Stephen Rago 维护,提供:
- 源代码下载(所有示例代码)
- 勘误表(errata)
- 各章摘要和更新说明
- 非常适合配合学习使用
- 购买纸质/电子书:
- Amazon:https://www.amazon.com/Advanced-Programming-UNIX-Environment-3rd/dp/0321637739
- 中国读者可在京东、当当购买中文翻译版(译者:戚正伟 等)
⚠️ 本书无“开源免费电子版”,请支持正版。
📜 2.《POSIX 标准文档》(IEEE Std 1003.1)
POSIX 是由 IEEE 和 The Open Group 共同维护的标准,官方文档需通过其网站获取。
✅ 官方免费在线查阅版(HTML):
👉 The Open Group Base Specifications, Issue 7 (2018) — POSIX.1-2017
🔗 https://pubs.opengroup.org/onlinepubs/9699919799/
这是当前最权威、最新且免费公开访问的 POSIX 标准文档(含 Shell、Utilities、System Interfaces、Headers 等)。
- 包含所有 pthread 函数规范(如
pthread_create
,pthread_mutex_lock
等) - 可直接搜索函数名,查看标准定义、参数、返回值、错误码、可移植性说明
- 支持书签、交叉引用,适合开发者查阅
📄 PDF 下载版(需注册,部分免费):
👉 https://publications.opengroup.org/standards/unix
- 注册后可免费下载 PDF(部分文档需付费)
- 搜索 “IEEE Std 1003.1™-2017” 或 “The Open Group Base Specifications Issue 7”
🆚 IEEE 官方标准购买页面(付费):
👉 https://standards.ieee.org/standard/1003_1-2017.html
- IEEE 出版的正式标准文档(PDF)
- 价格较高(约 $200+),适合企业或标准研究者
- 内容与 The Open Group 版本基本一致
✅ 对于绝大多数开发者,The Open Group 免费在线版已完全足够。
📌 补充:Linux man pages(在线)
虽然不是“标准文档”,但 Linux man pages 是最实用的函数参考:
🔗 https://man7.org/linux/man-pages/
- 搜索
pthread_create
→ https://man7.org/linux/man-pages/man3/pthread_create.3.html - 包含函数原型、描述、示例、错误码、遵循标准(如 POSIX.1-2001)
- 由 Michael Kerrisk(《The Linux Programming Interface》作者)维护,权威可靠
✅ 总结推荐
资源名称 | 类型 | 推荐链接 | 备注 |
---|---|---|---|
《APUE》官网 | 图书配套 | https://www.apuebook.com/ | 源码+勘误,必备 |
POSIX 标准在线文档 | 官方标准 | https://pubs.opengroup.org/onlinepubs/9699919799/ | 免费、权威、最新 |
Linux man-pages | 函数手册 | https://man7.org/linux/man-pages/ | 实用开发参考 |
IEEE POSIX 标准购买 | 付费标准 | https://standards.ieee.org/standard/1003_1-2017.html | 企业/研究用 |
📘 建议学习路径:
- 学 pthread → 查 man7.org 快速上手
- 深入理解标准行为 → 查 Open Group POSIX 文档
- 系统学习 UNIX 编程 → 读 APUE 第3版