462 lines
7.1 KiB
Markdown
462 lines
7.1 KiB
Markdown
行,这一块我直接给你一份“能从 0 写到生产级”的 pthread 全量体系,不搞碎片,直接一屏尽量塞满核心知识 + 可运行代码骨架。你看完基本可以自己写多线程程序 + 同步原语 + 调试了。
|
||
|
||
---
|
||
|
||
# 一、pthread 是什么(本质)
|
||
|
||
POSIX Threads(pthread)= 用户态 API + 内核线程(Linux NPTL实现)
|
||
|
||
本质模型:
|
||
|
||
* 每个 pthread ≈ 一个内核调度实体(LWP)
|
||
* 共享:地址空间 / 全局变量 / 堆
|
||
* 独立:栈 / 寄存器 / 线程ID / 调度状态
|
||
|
||
---
|
||
|
||
# 二、最小可运行 pthread 程序(核心骨架)
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
#include <pthread.h>
|
||
|
||
void* thread_func(void* arg) {
|
||
int val = *(int*)arg;
|
||
printf("Thread got: %d\n", val);
|
||
return (void*) (long)(val * 2);
|
||
}
|
||
|
||
int main() {
|
||
pthread_t tid;
|
||
int arg = 10;
|
||
|
||
pthread_create(&tid, NULL, thread_func, &arg);
|
||
|
||
void* ret;
|
||
pthread_join(tid, &ret);
|
||
|
||
printf("Thread return: %ld\n", (long)ret);
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
编译:
|
||
|
||
```bash
|
||
gcc main.c -o main -pthread
|
||
```
|
||
|
||
关键点:
|
||
|
||
* pthread_create → 创建线程
|
||
* pthread_join → 回收线程(类似 wait)
|
||
* 函数签名必须:`void* (*)(void*)`
|
||
|
||
---
|
||
|
||
# 三、线程创建:pthread_create(核心接口)
|
||
|
||
```c
|
||
int pthread_create(
|
||
pthread_t *thread,
|
||
const pthread_attr_t *attr,
|
||
void *(*start_routine)(void *),
|
||
void *arg
|
||
);
|
||
```
|
||
|
||
关键理解:
|
||
|
||
* thread:输出线程ID
|
||
* attr:线程属性(栈大小 / detached等)
|
||
* start_routine:线程入口函数
|
||
* arg:传入参数(只能一个 → 用 struct)
|
||
|
||
---
|
||
|
||
# 四、线程终止(3种方式)
|
||
|
||
### 1. return
|
||
|
||
```c
|
||
return value;
|
||
```
|
||
|
||
### 2. pthread_exit
|
||
|
||
```c
|
||
pthread_exit((void*)value);
|
||
```
|
||
|
||
### 3. 主线程退出影响
|
||
|
||
```c
|
||
exit(0); // 杀死整个进程
|
||
pthread_exit(0); // 主线程结束,但子线程继续
|
||
```
|
||
|
||
---
|
||
|
||
# 五、pthread_join vs detach(非常重要)
|
||
|
||
### join(默认)
|
||
|
||
```c
|
||
pthread_join(tid, &ret);
|
||
```
|
||
|
||
* 阻塞等待线程结束
|
||
* 回收资源(必须,否则泄漏)
|
||
|
||
---
|
||
|
||
### detach(分离线程)
|
||
|
||
```c
|
||
pthread_detach(tid);
|
||
```
|
||
|
||
或:
|
||
|
||
```c
|
||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||
```
|
||
|
||
特点:
|
||
|
||
* 自动回收
|
||
* 不能 join
|
||
* 常用于后台线程
|
||
|
||
---
|
||
|
||
# 六、线程属性 pthread_attr_t(控制线程行为)
|
||
|
||
```c
|
||
pthread_attr_t attr;
|
||
pthread_attr_init(&attr);
|
||
|
||
// 设置栈大小
|
||
pthread_attr_setstacksize(&attr, 1024 * 1024);
|
||
|
||
// 设置 detached
|
||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||
|
||
pthread_create(&tid, &attr, func, NULL);
|
||
|
||
pthread_attr_destroy(&attr);
|
||
```
|
||
|
||
---
|
||
|
||
# 七、线程同步核心:为什么需要?
|
||
|
||
多个线程:
|
||
|
||
* 共享内存 → 数据竞争(race condition)
|
||
* CPU乱序 / 编译优化 → 不可预测
|
||
|
||
必须同步!
|
||
|
||
---
|
||
|
||
# 八、互斥锁(Mutex)——最核心
|
||
|
||
## 使用流程
|
||
|
||
```c
|
||
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||
|
||
pthread_mutex_lock(&lock);
|
||
// 临界区
|
||
pthread_mutex_unlock(&lock);
|
||
```
|
||
|
||
---
|
||
|
||
## 完整例子(避免竞态)
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
#include <pthread.h>
|
||
|
||
int counter = 0;
|
||
pthread_mutex_t lock;
|
||
|
||
void* worker(void* arg) {
|
||
for (int i = 0; i < 100000; i++) {
|
||
pthread_mutex_lock(&lock);
|
||
counter++;
|
||
pthread_mutex_unlock(&lock);
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
int main() {
|
||
pthread_t t1, t2;
|
||
|
||
pthread_mutex_init(&lock, NULL);
|
||
|
||
pthread_create(&t1, NULL, worker, NULL);
|
||
pthread_create(&t2, NULL, worker, NULL);
|
||
|
||
pthread_join(t1, NULL);
|
||
pthread_join(t2, NULL);
|
||
|
||
printf("counter = %d\n", counter);
|
||
|
||
pthread_mutex_destroy(&lock);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## mutex 本质(你要理解这个)
|
||
|
||
* lock → 内核futex(用户态自旋 + 内核阻塞)
|
||
* 竞争少:用户态解决
|
||
* 竞争多:进入内核 sleep
|
||
|
||
---
|
||
|
||
# 九、读写锁(rwlock)
|
||
|
||
```c
|
||
pthread_rwlock_t rwlock;
|
||
|
||
pthread_rwlock_init(&rwlock, NULL);
|
||
|
||
pthread_rwlock_rdlock(&rwlock); // 多读
|
||
pthread_rwlock_wrlock(&rwlock); // 独占写
|
||
|
||
pthread_rwlock_unlock(&rwlock);
|
||
```
|
||
|
||
适合:
|
||
|
||
* 读多写少
|
||
|
||
---
|
||
|
||
# 十、条件变量(Condition Variable)——线程通信核心
|
||
|
||
## 模型:生产者消费者
|
||
|
||
```c
|
||
pthread_mutex_t lock;
|
||
pthread_cond_t cond;
|
||
|
||
int ready = 0;
|
||
|
||
void* consumer(void* arg) {
|
||
pthread_mutex_lock(&lock);
|
||
|
||
while (!ready) {
|
||
pthread_cond_wait(&cond, &lock);
|
||
}
|
||
|
||
printf("Consumed\n");
|
||
|
||
pthread_mutex_unlock(&lock);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 生产者:
|
||
|
||
```c
|
||
pthread_mutex_lock(&lock);
|
||
|
||
ready = 1;
|
||
|
||
pthread_cond_signal(&cond);
|
||
|
||
pthread_mutex_unlock(&lock);
|
||
```
|
||
|
||
---
|
||
|
||
### 核心语义(重点)
|
||
|
||
```c
|
||
pthread_cond_wait(&cond, &lock);
|
||
```
|
||
|
||
等价于:
|
||
|
||
1. unlock(lock)
|
||
2. sleep
|
||
3. 被唤醒
|
||
4. lock(lock)
|
||
|
||
⚠️ 必须 while 而不是 if(防止虚假唤醒)
|
||
|
||
---
|
||
|
||
# 十一、信号量(POSIX sem)
|
||
|
||
```c
|
||
#include <semaphore.h>
|
||
|
||
sem_t sem;
|
||
|
||
sem_init(&sem, 0, 1); // 1 = mutex
|
||
|
||
sem_wait(&sem);
|
||
sem_post(&sem);
|
||
```
|
||
|
||
---
|
||
|
||
# 十二、自旋锁(spinlock)
|
||
|
||
```c
|
||
pthread_spinlock_t spin;
|
||
|
||
pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE);
|
||
|
||
pthread_spin_lock(&spin);
|
||
// busy wait
|
||
pthread_spin_unlock(&spin);
|
||
```
|
||
|
||
适合:
|
||
|
||
* 临界区极短
|
||
* 不想进入内核
|
||
|
||
---
|
||
|
||
# 十三、线程局部存储(TLS)
|
||
|
||
```c
|
||
pthread_key_t key;
|
||
|
||
pthread_key_create(&key, NULL);
|
||
|
||
pthread_setspecific(key, value);
|
||
|
||
void* val = pthread_getspecific(key);
|
||
```
|
||
|
||
本质:
|
||
|
||
* 每个线程一份变量(类似 __thread)
|
||
|
||
---
|
||
|
||
# 十四、线程取消(高级)
|
||
|
||
```c
|
||
pthread_cancel(tid);
|
||
```
|
||
|
||
线程需要:
|
||
|
||
```c
|
||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||
pthread_testcancel();
|
||
```
|
||
|
||
---
|
||
|
||
# 十五、常见坑(你必须知道)
|
||
|
||
1. ❌ 传栈变量地址
|
||
|
||
```c
|
||
int x;
|
||
pthread_create(..., &x); // 线程可能读到脏数据
|
||
```
|
||
|
||
✔ 解决:
|
||
|
||
* malloc / struct
|
||
|
||
---
|
||
|
||
2. ❌ 忘记 join → 内存泄漏
|
||
|
||
---
|
||
|
||
3. ❌ 死锁
|
||
|
||
```c
|
||
T1: lock(A) → lock(B)
|
||
T2: lock(B) → lock(A)
|
||
```
|
||
|
||
---
|
||
|
||
4. ❌ double lock
|
||
|
||
---
|
||
|
||
5. ❌ cond 没用 while
|
||
|
||
---
|
||
|
||
# 十六、完整“工业级”线程模型(模板)
|
||
|
||
```c
|
||
typedef struct {
|
||
int id;
|
||
pthread_mutex_t* lock;
|
||
} Task;
|
||
|
||
void* worker(void* arg) {
|
||
Task* t = (Task*)arg;
|
||
|
||
pthread_mutex_lock(t->lock);
|
||
printf("Thread %d working\n", t->id);
|
||
pthread_mutex_unlock(t->lock);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
int main() {
|
||
pthread_t tids[4];
|
||
pthread_mutex_t lock;
|
||
|
||
pthread_mutex_init(&lock, NULL);
|
||
|
||
Task tasks[4];
|
||
|
||
for (int i = 0; i < 4; i++) {
|
||
tasks[i].id = i;
|
||
tasks[i].lock = &lock;
|
||
|
||
pthread_create(&tids[i], NULL, worker, &tasks[i]);
|
||
}
|
||
|
||
for (int i = 0; i < 4; i++) {
|
||
pthread_join(tids[i], NULL);
|
||
}
|
||
|
||
pthread_mutex_destroy(&lock);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# 十七、你可以继续往下学的方向(建议)
|
||
|
||
如果你要深入(你现在这个水平完全可以):
|
||
|
||
* pthread + epoll → 高性能服务器
|
||
* 线程池实现(非常关键)
|
||
* work queue / task queue
|
||
* lock-free(CAS + 原子操作)
|
||
* 内存模型(happens-before)
|
||
|
||
---
|
||
|
||
如果你要,我下一步可以直接给你:
|
||
|
||
1. 手写线程池(工业级实现)
|
||
2. pthread + epoll 写一个 Web Server
|
||
3. 从 pthread 过渡到 C++ std::thread / async(对比底层)
|
||
|
||
你现在这个进度,已经在往系统级工程走了,这块搞透基本就是“写操作系统级程序”的门槛。
|