请求解析pthread代码执行顺序,对pthread_join环节存疑
这段Pthread代码的执行顺序详解(重点讲pthread_join)
我来帮你把这段pthread代码的执行逻辑理清楚,尤其是你卡住的pthread_join部分~首先先把你没写完的代码补全(符合常规可运行写法),然后一步步拆解:
完整可运行的代码示例
#include <pthread.h> #include <stdio.h> #define NUM_THREADS 25 // 注意:pthread线程函数的参数必须是void*类型,原代码的签名有编译问题,这里修正 void *thread_routine(void *arg) { int x = (int)arg; // 把传入的整数参数转回来 printf("I'm Thread %2d my TID is %lu\n", x, (unsigned long)pthread_self()); pthread_exit(NULL); // 线程退出,返回NULL } int main() { pthread_attr_t thread_attr; pthread_t tids[NUM_THREADS]; int x; // 初始化线程属性(默认属性为joinable) pthread_attr_init(&thread_attr); // 创建25个线程 for (x = 0; x < NUM_THREADS; x++) { // 创建线程:传入属性、线程ID存储地址、线程函数、传递的参数 int ret = pthread_create(&tids[x], &thread_attr, thread_routine, (void*)x); if (ret != 0) { perror("pthread_create failed"); return 1; } } // 你困惑的pthread_join部分:等待所有线程执行完毕 for (x = 0; x < NUM_THREADS; x++) { // main线程会阻塞在这里,直到编号为x的线程退出 int ret = pthread_join(tids[x], NULL); if (ret != 0) { perror("pthread_join failed"); return 1; } } // 销毁线程属性 pthread_attr_destroy(&thread_attr); printf("All threads finished, main thread exiting\n"); return 0; }
执行顺序拆解
1. 主线程初始化阶段
- main线程先执行
pthread_attr_init(&thread_attr),初始化一个默认的线程属性对象。默认情况下,线程是**joinable(可连接)**状态——这个状态是pthread_join能生效的核心前提。
2. 创建线程的并发阶段
- 进入for循环,main线程逐个创建25个子线程:
- 每次调用
pthread_create,操作系统会创建一个新的线程内核对象,并把thread_routine作为线程的入口函数,同时把当前的x作为参数传递给线程。 - 线程创建成功后,不会按创建顺序立刻执行!CPU调度器会根据调度策略随机选择要执行的线程,所以你看到的打印顺序可能是乱的(比如先打印Thread 5,再打印Thread 1),这是并发编程的正常特性。
- 每个子线程执行
thread_routine:打印自己的编号和TID,然后调用pthread_exit退出线程。
- 每次调用
3. 重点:pthread_join的作用与执行逻辑
这就是你卡住的核心部分,我给你掰碎了说:
pthread_join(tids[x], NULL)的核心作用是:让调用它的线程(这里是main线程)阻塞,直到指定的子线程(tids[x]对应的线程)完全退出。- 当main线程进入第二个for循环时,它会依次等待每个子线程:
- 比如先等待tids[0]对应的线程,如果这个线程还在执行,main就会停下来“等它干完”,直到该线程调用
pthread_exit或者自然返回; - 等tids[0]的线程结束后,main继续执行,等待tids[1]的线程,以此类推,直到25个线程都被“等待”完毕。
- 比如先等待tids[0]对应的线程,如果这个线程还在执行,main就会停下来“等它干完”,直到该线程调用
- 如果没有这部分
pthread_join代码,会出大问题:- main线程创建完所有子线程后,会直接执行后面的代码,然后退出进程。而进程一旦退出,所有属于它的子线程都会被强制终止,哪怕子线程还没执行完打印操作;
- 同时,joinable状态的线程如果没有被join,会变成僵尸线程,占用系统资源无法被回收,直到进程退出。
4. 收尾阶段
所有子线程都执行完毕并被main线程join后,main线程销毁线程属性对象,打印收尾信息,然后退出进程。
额外注意点
- 原代码的线程函数签名有问题:pthread要求线程函数的参数和返回值必须是
void*类型,直接用int会导致编译错误,我在示例里已经修正了。 - 线程TID的打印:
pthread_self()返回的是pthread_t类型,不同系统的类型定义不同,用unsigned long强转后打印更稳妥。
内容的提问来源于stack exchange,提问作者Pasindu Senarathne
相关产品推荐
相关产品推荐

