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

7.1 KiB
Raw Permalink Blame History

行,这一块我直接给你一份“能从 0 写到生产级”的 pthread 全量体系,不搞碎片,直接一屏尽量塞满核心知识 + 可运行代码骨架。你看完基本可以自己写多线程程序 + 同步原语 + 调试了。


一、pthread 是什么(本质)

POSIX Threadspthread= 用户态 API + 内核线程Linux NPTL实现

本质模型:

  • 每个 pthread ≈ 一个内核调度实体LWP
  • 共享:地址空间 / 全局变量 / 堆
  • 独立:栈 / 寄存器 / 线程ID / 调度状态

二、最小可运行 pthread 程序(核心骨架)

#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;
}

编译:

gcc main.c -o main -pthread

关键点:

  • pthread_create → 创建线程
  • pthread_join → 回收线程(类似 wait
  • 函数签名必须:void* (*)(void*)

三、线程创建pthread_create核心接口

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

return value;

2. pthread_exit

pthread_exit((void*)value);

3. 主线程退出影响

exit(0);        // 杀死整个进程
pthread_exit(0); // 主线程结束,但子线程继续

五、pthread_join vs detach非常重要

join默认

pthread_join(tid, &ret);
  • 阻塞等待线程结束
  • 回收资源(必须,否则泄漏)

detach分离线程

pthread_detach(tid);

或:

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

特点:

  • 自动回收
  • 不能 join
  • 常用于后台线程

六、线程属性 pthread_attr_t控制线程行为

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——最核心

使用流程

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_lock(&lock);
// 临界区
pthread_mutex_unlock(&lock);

完整例子(避免竞态)

#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

pthread_rwlock_t rwlock;

pthread_rwlock_init(&rwlock, NULL);

pthread_rwlock_rdlock(&rwlock); // 多读
pthread_rwlock_wrlock(&rwlock); // 独占写

pthread_rwlock_unlock(&rwlock);

适合:

  • 读多写少

十、条件变量Condition Variable——线程通信核心

模型:生产者消费者

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

生产者:

pthread_mutex_lock(&lock);

ready = 1;

pthread_cond_signal(&cond);

pthread_mutex_unlock(&lock);

核心语义(重点)

pthread_cond_wait(&cond, &lock);

等价于:

  1. unlock(lock)
  2. sleep
  3. 被唤醒
  4. lock(lock)

⚠️ 必须 while 而不是 if防止虚假唤醒


十一、信号量POSIX sem

#include <semaphore.h>

sem_t sem;

sem_init(&sem, 0, 1); // 1 = mutex

sem_wait(&sem);
sem_post(&sem);

十二、自旋锁spinlock

pthread_spinlock_t spin;

pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE);

pthread_spin_lock(&spin);
// busy wait
pthread_spin_unlock(&spin);

适合:

  • 临界区极短
  • 不想进入内核

十三、线程局部存储TLS

pthread_key_t key;

pthread_key_create(&key, NULL);

pthread_setspecific(key, value);

void* val = pthread_getspecific(key);

本质:

  • 每个线程一份变量(类似 __thread

十四、线程取消(高级)

pthread_cancel(tid);

线程需要:

pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_testcancel();

十五、常见坑(你必须知道)

  1. 传栈变量地址
int x;
pthread_create(..., &x); // 线程可能读到脏数据

✔ 解决:

  • malloc / struct

  1. 忘记 join → 内存泄漏

  1. 死锁
T1: lock(A)  lock(B)
T2: lock(B)  lock(A)

  1. double lock

  1. cond 没用 while

十六、完整“工业级”线程模型(模板)

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对比底层

你现在这个进度,已经在往系统级工程走了,这块搞透基本就是“写操作系统级程序”的门槛。