目录
[pthread_t 类型详解表](#pthread_t 类型详解表)
[pthread_create - 创建线程](#pthread_create - 创建线程)
[pthread_join - 等待线程结束](#pthread_join - 等待线程结束)
[pthread_detach - 分离线程](#pthread_detach - 分离线程)
[pthread_exit - 线程退出](#pthread_exit - 线程退出)
[pthread_cancel - 取消线程](#pthread_cancel - 取消线程)
[pthread_self - 获取当前线程ID](#pthread_self - 获取当前线程ID)
[pthread_equal - 比较线程ID](#pthread_equal - 比较线程ID)
1.线程的其他相关概念
线程与轻量级进程
- 线程是Linux内核调度的基本单位,在底层通过轻量级进程(LWP)实现
| 特性 | 线程 (Thread) | 轻量级进程 (LWP) |
| 概念层级 | 用户级概念,POSIX标准定义 | 内核级概念,Linux实现机制 |
| 实现方式 | Linux中通过轻量级进程实现 | Linux内核中线程的实际形态 |
| 创建方式 | pthread_create() | clone()系统调用(设置CLONE_VM等标志) |
| 资源开销 | 较小(共享大部分资源) | 较小(相比进程)但比纯用户线程大 |
| 资源隔离 | 最低(共享地址空间、文件描述符等) | 最低(同一进程的LWP共享资源) |
|---|
原生线程库
用户空间
↓ 创建线程(pthread_create)
pthread库(用户层)
↓ 封装为clone()系统调用
内核空间
↓ 创建LWP(设置CLONE_VM等标志)
调度器调度LWP执行
用户空间:
├── pthread库(共享区加载)
│ ├── 线程控制块(TCB)管理 ← pthread_t指向这里
│ ├── 线程栈分配(共享区内)
│ └── 线程属性管理
└── 应用程序代码
内核空间:
└── LWP调度与执行(通过clone()创建)
内核通过clone()等系统调用来创建任务,根据资源共享程度的不同表现为进程或线程。为了向用户提供符合POSIX标准的线程接口 ,Linux在应用层提供了原生线程库(pthread)
-
pthread 库几乎是任何Linux平台默认自带的,在多线程编程时必须链接使用该库(-lpthread)
编译时需要链接pthread库
gcc thread.c -o thread -lpthread
C++11多线程程序也需要链接
g++ thread.cpp -o thread -std=c++11 -lpthread
内核仅提供轻量级进程(LWP)这一底层机制,而用户需要符合POSIX标准的、易用的线程接口。pthread库正是为了解决这一鸿沟而存在,它将内核的LWP机制封装成标准化的线程API,管理用户空间的线程控制块(TCB)、线程栈和同步原语,同时隐藏了clone()系统调用的复杂性
线程ID

| ID层次 | 获取方式 | 本质 | 特点 |
|---|---|---|---|
| 用户态线程ID (pthread_t) | pthread_self() |
TCB在共享区中的起始地址 | 进程内唯一,用户空间标识 |
| 内核态线程ID (LWP ID) | syscall(SYS_gettid) |
内核LWP的PID | 系统全局唯一,调度实体 |
| 线程组ID (进程PID) | getpid() |
主线程的LWP ID | 所有线程相同,标识进程 |
__thread关键字
_thread用于定义线程局部存储(Thread-Local Storage, TLS)变量 。使用__thread修饰的变量,每个线程都拥有其独立的副本,线程间互不干扰 。主要适用于内置基本类型 (如 int、char、float、指针等)以及用编译时常量初始化的简单结构体 ,不支持动态初始化、动态内存分配或复杂对象。 这些变量的存储空间位于各线程自己的线程控制块(TCB)或专门的TLS段中,生命周期与线程相同,线程结束时自动释放。__thread通常用于存储线程特有的上下文信息,如线程ID、错误状态或线程局部缓存,能有效避免多线程环境下的数据竞争
栈分配机制对比
| 线程类型 | 栈位置 | 分配方式 | 大小 |
|---|---|---|---|
| 主线程 | 进程地址空间栈区 | 进程启动时分配 | 由系统限制决定 |
| 新线程 | 共享库映射区 | pthread库动态分配 | 默认8MB,可通过属性设置 |
每一个线程在调用时都必须拥有独立的栈结构,因为每一个线程都会有自己的调用链,保存运行时的临时变量
线程与信号
- 在Linux系统中,当向进程(通过PID) 发送信号时,信号被递送给整个线程组,通常由主线程处理或任一线程处理(取决于信号处理设置)。当向特定线程(通过LWP ID) 发送信号时,信号仅递送给该线程,但致命信号(如SIGKILL、SIGTERM、SIGSTOP) 无论发送目标是进程还是线程,都会导致整个进程终止,这是因为这些信号作用于进程级别,内核会终止所有线程。而**非致命信号(如SIGUSR1、SIGUSR2)**发送给特定线程时,只有该线程会响应和处理。因此,虽然可以指定信号发送给特定线程,但对于进程级控制的信号,线程作为进程的执行分支,最终仍体现为进程级别的行为
线程与程序替换
- 在Linux多线程进程中,任何一个线程调用 exec 系列函数(如 execve, execl, execvp 等)都会导致整个进程的代码和数据被完全替换,所有线程立即终止,只有调用 exec 的线程继续执行新程序
2.线程管理函数
pthread_t 类型详解表
| 项目 | 说明 |
|---|---|
| 类型定义 | typedef unsigned long int pthread_t; (Linux常见实现) |
| 本质 | POSIX线程标识符类型,用于唯一标识进程内的一个线程 |
| 特性 | 不透明数据类型 (opaque type),用户不应直接操作其内部值 |
| 内部实现 | 在Linux NPTL实现中,通常是线程控制块(TCB)在共享区的起始地址 |
| 大小 | sizeof(pthread_t) 通常是8字节(64位系统)或4字节(32位系统) |
| 比较方式 | 必须使用 pthread_equal(t1, t2) 函数,不能用 == 运算符 |
| 获取方式 | 1. 创建时获取: pthread_create(&tid, ...) 2. 当前线程: pthread_self() |
| 作用范围 | 进程内有效,不能跨进程识别线程 |
| 与内核ID的关系 | 不同于内核LWP ID (gettid()获取) 和进程PID (getpid()获取) |
| 生命周期 | 从线程创建开始有效,到线程结束后变为无效(但ID值仍可能被重用) |
| 打印方式 | printf("%lu", (unsigned long)tid); 或使用专用格式化函数 |
| 用途 | 1. 线程创建、等待、取消 2. 线程资源管理 3. 线程间标识和区分 |
不同ID的区别
| ID类型 | 获取函数 | 作用域 | 本质 | 用途 |
|---|---|---|---|---|
pthread_t |
pthread_self() |
进程内 | 用户态线程库的线程句柄 | 线程库API操作 |
| LWP ID | syscall(SYS_gettid) |
系统全局 | 内核调度实体ID | 系统级线程调度、ps命令查看 |
| 进程PID | getpid() |
系统全局 | 进程标识符 | 进程管理、进程间通信 |
| 线程组ID | getpid() (主线程) |
系统全局 | 主线程的PID | 标识整个线程组 |
使用示例表
| 操作 | 代码示例 | 说明 |
|---|---|---|
| 创建线程 | pthread_create(&tid, NULL, func, arg); |
tid 存储新线程ID |
| 获取自身ID | pthread_t myid = pthread_self(); |
返回调用线程的ID |
| 比较线程ID | if (pthread_equal(id1, id2)) {...} |
正确比较方式 |
| 错误比较 | if (id1 == id2) {...} |
错误! 可能工作但不保证 |
| 格式化输出 | printf("Thread ID: %lu\n", (unsigned long)tid); |
转换为数值打印 |
| 存储为字符串 | char buf[32]; snprintf(buf, sizeof(buf), "%lu", (unsigned long)tid); |
便于日志记录 |
pthread_create - 创建线程
| 项目 | 说明 |
|---|---|
| 函数原型 | int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) |
| 功能 | 创建一个新的线程 |
| 参数说明 | - thread: 指向线程ID的指针,用于存储新线程的ID - attr: 线程属性指针,NULL表示默认属性 - start_routine: 线程函数的地址,格式为void func(void) - arg: 传递给线程函数的参数** |
| 返回值 | 成功: 0 失败: 返回错误编号(正整数),不设置errno |
| 注意事项 | 1. 线程创建后立即开始执行 2. 线程函数必须返回void* 3. 主线程应使用pthread_join或pthread_detach处理新线程 |
pthread_create的最后一个参数传值,不取地址不使用共同变量传参,因为不知道哪一个线程先调度
pthread_join - 等待线程结束
| 项目 | 说明 |
|---|---|
| 函数原型 | int pthread_join(pthread_t thread, void **retval) |
| 功能 | 阻塞等待指定线程结束,并回收线程资源 |
| 参数说明 | - thread: 要等待的线程ID - retval: 接收线程返回值的二级指针,NULL表示不关心返回值 |
| 返回值 | 成功 : 0 失败 : 返回错误编号(正整数),不设置errno |
| 注意事项 | 1. 线程必须是非分离状态 (joinable) 2. 一个线程只能被一个线程join一次 3. 避免线程僵尸(类似进程僵尸) |
- 在Linux多线程编程中,线程的调度顺序由操作系统决定
- 为避免资源泄漏,必须正确处理线程:使用
pthread_join()显式等待 线程结束并回收资源,或使用pthread_detach()设置线程为分离状态使其自动回收- 线程结束后如果既未join也未detach,会导致pthread库分配的资源(线程栈、TCB等)泄漏
- 这种泄漏完全无法通过系统命令(如ps)查看或检测,因为线程资源由pthread库在用户空间管理,不反映在内核的进程表中,只能通过内存分析工具或程序监控间接发现

pthread_detach - 分离线程
| 项目 | 说明 |
|---|---|
| 函数原型 | int pthread_detach(pthread_t thread) |
| 功能 | 将线程设置为分离状态,线程结束时系统自动回收资源 |
| 参数说明 | thread: 要分离的线程ID |
| 返回值 | 成功 : 0 失败 : 返回错误编号(正整数),不设置errno |
| 注意事项 | 1. 分离后不能再用pthread_join等待 2. 资源在线程结束时自动释放 3. 适用于不需要等待结果的线程 |
pthread_exit - 线程退出
| 项目 | 说明 |
|---|---|
| 函数原型 | void pthread_exit(void *retval) |
| 功能 | 终止当前线程的执行 |
| 参数说明 | retval: 线程的返回值,可被pthread_join获取 |
| 返回值 | 无返回值(函数不返回) |
| 注意事项 | 1. 与return不同,return只能返回线程函数 2. 主线程调用pthread_exit(),主线程终止但其他线程继续运行,直到所有线程结束进程才退出 3. 清理函数会被调用 |
退出函数比较
| 调用线程 | 函数 | 效果 |
|---|---|---|
| 任何线程 | exit(status) |
整个进程立即终止,所有线程强制结束 |
| 任何线程 | pthread_exit(retval) |
子线程调用 pthread_exit:仅终止自身。 主线程调用 pthread_exit:主线程终止,但进程挂起等待其他非分离子线程结束后才退出。 |
| 主线程 | return |
主线程 return 等同于 exit(),会直接导致整个进程终止,所有子线程被强制结束 |
pthread_cancel - 取消线程
| 项目 | 说明 |
|---|---|
| 函数原型 | int pthread_cancel(pthread_t thread) |
| 功能 | 向指定线程发送取消请求 |
| 参数说明 | thread: 要取消的线程ID |
| 返回值 | 成功 : 0 失败 : 返回错误编号(正整数),不设置errno |
| 注意事项 | 1. 线程在取消点才会响应取消请求(陷入内核的系统调用或特定函数设置) 2. 默认取消类型是延迟取消 3. 线程被取消后,通过pthread_join获取的返回值是PTHREAD_CANCELED宏(定义为((void *)-1)) |
pthread_self - 获取当前线程ID
| 项目 | 说明 |
|---|---|
| 函数原型 | pthread_t pthread_self(void) |
| 功能 | 获取调用线程自身的ID |
| 参数说明 | 无 |
| 返回值 | 当前线程的ID |
| 注意事项 | 1. 线程ID只在进程内唯一 2. 可用于线程间的比较(需使用pthread_equal) 3**. 主线程的ID不一定等于进程ID** |
ID比较
| ID类型 | 获取函数 | 说明 | 主线程的情况 |
|---|---|---|---|
| pthread_t (用户态线程ID) | pthread_self() |
pthread库分配的线程标识符 | 不等于进程PID |
| LWP ID (内核线程ID) | syscall(SYS_gettid) |
内核调度的轻量级进程ID | 等于进程PID |
| 进程PID (线程组ID) | getpid() |
进程标识符,所有线程相同 | 等于主线程LWP ID |
pthread_equal - 比较线程ID
| 项目 | 说明 |
|---|---|
| 函数原型 | int pthread_equal(pthread_t t1, pthread_t t2) |
| 功能 | 比较两个线程ID是否相等 |
| 参数说明 | t1, t2: 要比较的两个线程ID |
| 返回值 | 相等: 非0值(通常是1) 不相等: 0 |
| 注意事项 | 1. pthread_t是不透明类型,不能用==比较 2. 通常在进程内比较有效 |