Files
2026-03-22 17:40:44 +08:00

462 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
行,这一块我直接给你一份“能从 0 写到生产级”的 pthread 全量体系,不搞碎片,直接一屏尽量塞满核心知识 + 可运行代码骨架。你看完基本可以自己写多线程程序 + 同步原语 + 调试了。
---
# 一、pthread 是什么(本质)
POSIX Threadspthread= 用户态 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-freeCAS + 原子操作)
* 内存模型happens-before
---
如果你要,我下一步可以直接给你:
1. 手写线程池(工业级实现)
2. pthread + epoll 写一个 Web Server
3. 从 pthread 过渡到 C++ std::thread / async对比底层
你现在这个进度,已经在往系统级工程走了,这块搞透基本就是“写操作系统级程序”的门槛。