线程与同步

一、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);

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

五、常见并发模式

1. 生产者 / 消费者

2. 读者写者

3. Future / Promise

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