一、POSIX 线程库概述

Linux 下的线程函数都属于 POSIX 线程库(pthread),绝大多数函数名以pthread_开头。使用时需注意:

  • 头文件:#include <pthread.h>
  • 编译链接:需加-lpthread选项(如gcc main.c -o main -lpthread
  • 错误处理:pthread 函数出错时不设置全局 errno,而是通过返回值直接返回错误码(读取返回值比读取线程内 errno 开销更小)。

二、核心线程控制函数详解

1. pthread_create —— 创建线程

功能

在进程中创建一个新的执行流(线程),新线程会立即执行指定的回调函数。

函数原型
int pthread_create(
    pthread_t *thread,        // 输出参数:返回新线程的ID
    const pthread_attr_t *attr, // 线程属性,NULL表示使用默认属性
    void *(*start_routine)(void*), // 线程启动后执行的函数指针
    void *arg                  // 传给start_routine的参数
);
参数详解
  • thread:指向pthread_t类型变量的指针。

    • 结合课件底层原理:pthread_t本质是进程地址空间中线程控制块(TCB)的地址(对应 glibc 的struct pthread*)。
    • 新线程创建成功后,其 TCB 地址会被写入该指针指向的变量。
  • attr:线程属性对象指针,通常传NULL使用默认属性。

    • 可设置栈大小、分离状态、调度优先级等(后文会单独讲属性函数)。
  • start_routine:线程入口函数指针,类型为void* (*)(void*)

    • 线程启动后会自动调用该函数,函数返回 / 退出时线程终止。
  • arg:传给start_routine的参数。

    • 若需传多个参数,可封装为结构体后传指针。
返回值
  • 成功:返回0
  • 失败:返回错误码(如EAGAIN表示系统资源不足,EINVAL表示属性无效)。
底层原理

根据课件中 glibc 源码分析,pthread_create内部主要做三件事:

  1. 分配 TCB 与栈:调用ALLOCATE_STACK宏,通过mmap在进程共享区分配线程栈和 TCB(struct pthread);
  2. 填充 TCB:设置入口函数start_routine、参数arg、线程 ID 等;
  3. 调用 clone 系统调用:内核创建轻量级进程(LWP),并开始执行线程入口逻辑。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 线程入口函数
void* thread_func(void* arg) {
    char* msg = (char*)arg;
    for (int i = 0; i < 3; i++) {
        printf("%s: 第%d次执行\n", msg, i+1);
        sleep(1);
    }
    return NULL;
}

int main() {
    pthread_t tid;
    // 创建线程,传入参数"新线程"
    int ret = pthread_create(&tid, NULL, thread_func, "新线程");
    if (ret != 0) {
        fprintf(stderr, "创建线程失败: %d\n", ret);
        return 1;
    }

    // 主线程也执行
    thread_func("主线程");
    return 0;
}

2. pthread_self —— 获取当前线程 ID

功能

获取当前调用线程的pthread_t类型 ID。

函数原型
pthread_t pthread_self(void);
关键说明
  • 返回的pthread_t用户态线程库维护的 ID(TCB 地址),而非内核的 LWP ID;
  • 内核 LWP ID 可通过syscall(SYS_gettid)获取(需包含<sys/syscall.h>);
  • 结合课件:用ps -aL可查看线程的 LWP(LWP列),其中与PID相同的是主线程。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <unistd.h>

void* thread_func(void* arg) {
    pthread_t tid = pthread_self();
    pid_t lwp = syscall(SYS_gettid); // 获取内核LWP ID
    printf("用户态线程ID: %p, 内核LWP ID: %d\n", (void*)tid, lwp);
    return NULL;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);
    pthread_join(tid, NULL);
    return 0;
}

3. pthread_exit —— 终止当前线程(3、6)

功能

主动终止当前调用线程,而不影响进程内其他线程。

函数原型
void pthread_exit(void *value_ptr);
参数与注意事项
  • value_ptr:线程的退出值,会被pthread_join获取。
    • ⚠️ 绝对不能指向线程栈上的局部变量:线程退出后栈空间会被回收,该地址变为无效;
    • 应指向全局变量动态分配内存(malloc)静态变量
return的区别
  • 在线程入口函数中return也会终止线程,返回值同样会被pthread_join获取;
  • pthread_exit可以在入口函数的子函数中调用,直接终止线程(return只能从当前子函数返回)。
底层原理(结合课件)

线程退出时,会将value_ptr写入自己 TCB 的result字段,等待pthread_join来读取。

使用示例
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

void* thread_func(void* arg) {
    int* ret = malloc(sizeof(int));
    *ret = 100; // 退出值设为100
    pthread_exit(ret); // 终止线程,返回ret
    // 下面的代码不会执行
    printf("这行不会打印\n");
    return NULL;
}

int main() {
    pthread_t tid;
    void* result;
    pthread_create(&tid, NULL, thread_func, NULL);
    pthread_join(tid, &result);
    printf("线程退出值: %d\n", *(int*)result);
    free(result); // 记得释放动态内存
    return 0;
}

4. pthread_join —— 等待指定线程终止

pthread_joni使用理解与误区讲解
功能

阻塞当前线程,直到指定的线程终止,并获取其退出值。

函数原型
int pthread_join(pthread_t thread, void **value_ptr);
参数详解
  • thread:要等待的线程 ID(pthread_t类型)。
  • value_ptr:二级指针,输出参数
    • 若不为NULL,线程的退出值会被写入该指针指向的void*变量;
    • 结合之前的误区:这里是 “通过指针修改另一个指针的值”,因此必须用二级指针。
返回值
  • 成功:返回0
  • 失败:返回错误码(如ESRCH表示线程不存在,EINVAL表示线程不可 join)。
线程终止状态与退出值的对应(结合课件)

根据课件内容,线程终止方式不同,value_ptr获取到的值也不同:

表格

线程终止方式 *value_ptr的值
入口函数return返回 线程函数的返回值
调用pthread_exit终止 pthread_exit的参数value_ptr
pthread_cancel取消 常量PTHREAD_CANCELED(void*)-1
关键注意事项
  • 线程默认是joinable状态,必须被pthread_join,否则线程退出后资源(TCB、栈)无法释放,造成内存泄漏;
  • 一个线程只能被pthread_join一次,多次 join 会导致未定义行为。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

// 方式1:return返回
void* thread1(void* arg) {
    int* ret = malloc(sizeof(int));
    *ret = 1;
    return ret;
}

// 方式2:pthread_exit终止
void* thread2(void* arg) {
    int* ret = malloc(sizeof(int));
    *ret = 2;
    pthread_exit(ret);
}

// 方式3:被cancel取消
void* thread3(void* arg) {
    while (1) {
        printf("线程3运行中...\n");
        sleep(1); // sleep是取消点
    }
    return NULL;
}

int main() {
    pthread_t tid;
    void* result;

    // 测试return
    pthread_create(&tid, NULL, thread1, NULL);
    pthread_join(tid, &result);
    printf("线程1退出值: %d\n", *(int*)result);
    free(result);

    // 测试pthread_exit
    pthread_create(&tid, NULL, thread2, NULL);
    pthread_join(tid, &result);
    printf("线程2退出值: %d\n", *(int*)result);
    free(result);

    // 测试cancel
    pthread_create(&tid, NULL, thread3, NULL);
    sleep(3);
    pthread_cancel(tid); // 取消线程3
    pthread_join(tid, &result);
    if (result == PTHREAD_CANCELED) {
        printf("线程3被取消\n");
    }

    return 0;
}

5. pthread_detach —— 分离线程

功能

将指定线程设置为detached(分离)状态,线程退出时系统会自动释放其资源,无需pthread_join

函数原型
int pthread_detach(pthread_t thread);
参数与返回值
  • thread:要分离的线程 ID;
  • 返回值:成功返回0,失败返回错误码。
关键说明
  • 可以由其他线程调用pthread_detach(tid)分离目标线程,也可以由线程自身调用pthread_detach(pthread_self())
  • joinable 与 detached 冲突:一个线程不能同时是两种状态,分离后不能再pthread_join
  • 若不关心线程返回值,推荐使用分离状态,避免资源泄漏。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* thread_func(void* arg) {
    pthread_detach(pthread_self()); // 自身分离
    printf("分离线程运行,退出后自动释放资源\n");
    return NULL;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);
    sleep(1); // 等待线程执行完
    // 无需pthread_join
    return 0;
}

6. pthread_cancel —— 终止指定线程

功能

向指定线程发送取消请求,请求其终止执行。

函数原型
int pthread_cancel(pthread_t thread);
关键说明
  • pthread_cancel只是发送请求,不会等待线程终止;
  • 线程是否响应取消、何时响应,取决于取消状态取消类型
    • 取消状态:默认是PTHREAD_CANCEL_ENABLE(允许取消),可设为PTHREAD_CANCEL_DISABLE(禁止取消);
    • 取消类型:默认是PTHREAD_CANCEL_DEFERRED(延迟取消,到达取消点才终止),可设为PTHREAD_CANCEL_ASYNCHRONOUS(异步取消,立即终止)。
  • 取消点:线程会检查取消请求的函数,如sleepreadwritepthread_testcancel等。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* thread_func(void* arg) {
    // 设置为允许取消(默认),延迟取消(默认)
    while (1) {
        printf("线程运行中...\n");
        sleep(1); // sleep是取消点,会在此处响应取消
    }
    return NULL;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);
    sleep(3);
    pthread_cancel(tid); // 发送取消请求
    pthread_join(tid, NULL); // 等待线程终止
    printf("线程已取消\n");
    return 0;
}

三、线程属性函数(扩展)

课件中提到了struct pthread_attr结构体,用于设置线程属性。常用属性函数如下:

1. 初始化 / 销毁属性对象

int pthread_attr_init(pthread_attr_t *attr);    // 初始化属性为默认值
int pthread_attr_destroy(pthread_attr_t *attr); // 销毁属性对象

2. 设置 / 获取分离状态

// 设置分离状态:PTHREAD_CREATE_JOINABLE(默认)或PTHREAD_CREATE_DETACHED
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);

3. 设置 / 获取栈大小

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
  • 默认栈大小通常为 8MB,可根据需求调整(如嵌入式环境可减小栈大小)。

属性使用示例

#include <stdio.h>
#include <pthread.h>

void* thread_func(void* arg) {
    printf("分离线程运行\n");
    return NULL;
}

int main() {
    pthread_t tid;
    pthread_attr_t attr;
    
    // 初始化属性
    pthread_attr_init(&attr);
    // 设置为分离状态
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
    // 用属性创建线程
    pthread_create(&tid, &attr, thread_func, NULL);
    
    // 销毁属性
    pthread_attr_destroy(&attr);
    
    // 无需join,线程退出自动释放资源
    pthread_exit(NULL); // 主线程退出,不影响新线程
    return 0;
}

四、调试辅助函数(非标准)

介绍两个用于调试的非标准函数(_np表示 “non-portable”,不可移植):

// 设置当前线程名称(Linux下最大16字符,包括\0)
int pthread_setname_np(pthread_t thread, const char *name);
// 获取当前线程名称
int pthread_getname_np(pthread_t thread, char *name, size_t len);
  • ps -aL可查看线程名称,方便多线程调试。

五、常见函数使用总结与避坑指南

  1. 错误处理:始终检查 pthread 函数的返回值,不要依赖 errno;
  2. 返回值内存管理:线程返回值若指向动态内存,主线程join后需free
  3. 避免局部变量返回pthread_exitreturn的参数不能是线程栈局部变量;
  4. 资源释放:joinable 线程必须join,或设为 detached 状态,否则内存泄漏;
  5. 取消安全:使用pthread_cancel时注意取消点,避免资源未释放就终止。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐