线程与同步
一、POSIX Threads(pthread)
1. 创建与等待
#include <pthread.h>
void *worker(void *arg) {
int id = (int)(intptr_t)arg;
printf("thread %d\n", id);
return NULL;
}
pthread_t tid;
pthread_create(&tid, NULL, worker, (void*)(intptr_t)1);
pthread_join(tid, NULL); // 阻塞回收
pthread_detach(tid); // 自动回收(不 join)
2. 一次性初始化
pthread_once_t once = PTHREAD_ONCE_INIT;
void init(void) { /* ... */ }
pthread_once(&once, init);
3. 线程局部存储(TLS)
__thread int my_var; // GCC 扩展
_Thread_local int my_var; // C11
4. 线程属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1024*1024);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, fn, arg);
pthread_attr_destroy(&attr);
二、同步原语
1. Mutex(互斥锁)
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&m);
// 临界区
pthread_mutex_unlock(&m);
// 死锁预防:lock ordering、try lock、超时
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 1;
int rc = pthread_mutex_timedlock(&m, &ts);
类型:
- PTHREAD_MUTEX_NORMAL:默认,无死锁检测
- PTHREAD_MUTEX_ERRORCHECK:检错
- PTHREAD_MUTEX_RECURSIVE:可重入
- PTHREAD_MUTEX_DEFAULT = NORMAL
2. 条件变量
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
int ready = 0;
void producer(void) {
pthread_mutex_lock(&m);
ready = 1;
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);
}
void consumer(void) {
pthread_mutex_lock(&m);
while (!ready) // 必须是 while
pthread_cond_wait(&c, &m); // 自动解锁并睡眠
// 处理
pthread_mutex_unlock(&m);
}
pthread_cond_broadcast(&c); // 唤醒所有
3. 读写锁
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
pthread_rwlock_rdlock(&rw);
// 读
pthread_rwlock_unlock(&rw);
pthread_rwlock_wrlock(&rw);
// 写
pthread_rwlock_unlock(&rw);
4. 自旋锁
pthread_spinlock_t s;
pthread_spin_init(&s, PTHREAD_PROCESS_SHARED);
pthread_spin_lock(&s); // 忙等
pthread_spin_unlock(&s);
- 不释放 CPU,不能持锁睡眠
- 适用:临界区极短、多核
5. 屏障(Barrier)
pthread_barrier_t b;
pthread_barrier_init(&b, NULL, N); // N 个线程同步
pthread_barrier_wait(&b);
pthread_barrier_destroy(&b);
6. Semaphore(POSIX 信号量)
#include <semaphore.h>
sem_t sem;
sem_init(&sem, 0, 1); // 0 = 线程间
sem_wait(&sem); // P
sem_post(&sem); // V
sem_destroy(&sem);
// 命名信号量(进程间)
sem_t *s = sem_open("/my_sem", O_CREAT, 0644, 1);
sem_wait(s);
sem_post(s);
sem_close(s);
sem_unlink("/my_sem");
三、原子操作(C11 / GCC 内建)
#include <stdatomic.h>
atomic_int counter = 0;
atomic_fetch_add(&counter, 1); // 返回旧值
atomic_load(&counter);
atomic_store(&counter, 10);
atomic_compare_exchange_strong(&counter, &expected, desired);
// GCC 内建(更底层)
__sync_fetch_and_add(&counter, 1);
__sync_bool_compare_and_swap(&counter, old, new);
__sync_synchronize(); // 内存屏障
四、内存模型与屏障
__atomic_thread_fence(memory_order_acquire);
__atomic_thread_fence(memory_order_release);
__atomic_thread_fence(memory_order_seq_cst);
- Acquire-Release:同步生产者/消费者
- Sequential Consistency:最强,默认
五、常见并发模式
1. 生产者 / 消费者
- 条件变量 + 队列
- 信号量
- 无锁环形队列
2. 读者写者
- pthread_rwlock
- seqlock(Linux 内核用)
3. Future / Promise
- pthread_cond + 共享状态
std::future(C++)
4. 线程池
struct threadpool {
pthread_t *threads;
int n;
struct job *head, *tail;
pthread_mutex_t m;
pthread_cond_t c;
};
六、错误处理
int rc = pthread_mutex_lock(&m);
if (rc != 0) errno = rc; // 注意:POSIX 函数不直接用 errno
// 但可以通过 strerror(rc) 取文本
fprintf(stderr, "lock: %s\n", strerror(rc));
七、踩坑清单
| 现象 | 原因 |
|---|---|
| 死锁 | 锁顺序不一致 / 忘记释放 / 递归锁 + 不可重入 |
| 性能差 | 锁粒度太大 |
| 数据竞争 | 共享变量未同步 |
| A-B-A 问题 | CAS 在多值变量上失败 |
| pthread_cond 丢失唤醒 | 不在 while 循环中 wait |
| futex 锁争抢 | 长临界区用 futex,短临界区用 spin |