一文打尽pthread库

一文打尽pthread库

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

pthread(POSIX Threads)是遵循 POSIX 标准的线程库,广泛用于 Unix/Linux 系统中实现多线程编程。它提供了一组 C 语言 API,用于创建、管理、同步线程。以下是对 pthread 库及相关函数的系统性总结:

一、基本概念

  • 线程(Thread):轻量级进程,共享进程地址空间,独立执行流。

  • 主线程:程序启动时默认创建的线程。

  • 并发 vs 并行:并发是逻辑上同时执行,并行是物理上同时执行(多核)。

  • 线程安全:函数/数据结构在多线程环境下能正确工作。

二、核心数据类型

1
2
3
4
5
6
7
#include <pthread.h>

pthread_t // 线程标识符(类似进程的 pid)
pthread_attr_t // 线程属性结构体
pthread_mutex_t // 互斥锁
pthread_cond_t // 条件变量
pthread_rwlock_t // 读写锁

三、线程管理函数

1. 创建线程

1
2
3
4
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. 等待线程结束(阻塞)

1
int pthread_join(pthread_t thread, void **retval);
  • 阻塞调用线程,直到目标线程结束。

  • 可获取线程返回值(通过 retval)。

  • 适用于需要同步或回收资源的场景。

3. 分离线程(非阻塞回收)

1
int pthread_detach(pthread_t thread);
  • 线程结束后自动释放资源,无需 pthread_join。

  • 不能对已分离线程调用 pthread_join。

4. 获取当前线程 ID

1
pthread_t pthread_self(void);

5. 比较线程 ID

1
int pthread_equal(pthread_t t1, pthread_t t2);
  • 返回非 0 表示相等。

6. 退出线程

1
void pthread_exit(void *retval);
  • 主动终止当前线程,可传递返回值。

  • 主线程调用 pthread_exit 不会终止整个进程(除非是最后一个线程)。

四、线程属性设置(可选)

1
2
3
4
5
6
7
8
9
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)

用于保护临界区,防止多个线程同时访问共享资源。

1
2
3
4
5
6
7
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);

🔒 使用模式:

1
2
3
pthread_mutex_lock(&mutex);
// 临界区代码
pthread_mutex_unlock(&mutex);

2. 条件变量(Condition Variable)

用于线程间通信,常与互斥锁配合使用,实现“等待某个条件成立”。

1
2
3
4
5
6
7
8
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);

🔄 典型使用模式:

1
2
3
4
5
6
pthread_mutex_lock(&mutex);
while (condition_is_false) {
pthread_cond_wait(&cond, &mutex); // 自动释放锁,被唤醒后重新加锁
}
// 执行操作
pthread_mutex_unlock(&mutex);

3. 读写锁(Read-Write Lock)

允许多个读者同时访问,写者独占访问。

1
2
3
4
5
6
7
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)【可选】

忙等待锁,适用于锁持有时间极短的场景。

1
2
3
4
5
6
pthread_spinlock_t spinlock;

int pthread_spin_init(...);
int pthread_spin_lock(...);
int pthread_spin_unlock(...);
int pthread_spin_destroy(...);

六、线程局部存储(TLS)

每个线程拥有独立的变量副本。

1
2
3
4
5
6
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)

允许一个线程请求终止另一个线程。

1
2
3
4
5
6
7
8
9
10
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)

确保某段初始化代码只执行一次。

1
2
3
pthread_once_t once_control = PTHREAD_ONCE_INIT;

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

常用于初始化全局资源(如互斥锁、TLS key)。

九、常见错误处理

pthread 函数不设置 errno,而是直接返回错误码:

1
2
3
4
int ret = pthread_create(...);
if (ret != 0) {
fprintf(stderr, "Error: %s\n", strerror(ret));
}

或使用 perror 需要先设置 errno:

1
2
errno = ret;
perror("pthread_create");

十、编译与链接

1
2
3
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 检测竞争条件。

十二、简单示例

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
#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)

各章摘要和更新说明

非常适合配合学习使用

⚠️ 本书无“开源免费电子版”,请支持正版。

📜 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/

✅ 总结推荐

资源名称类型推荐链接备注《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版

data-ad-format="auto" data-full-width-responsive="true">