线程的创建#include pthread.hint pthread_create(pthread_t *thread, constpthread_attr_t *attr, void *(*routine)(void *), void *arg);成功返回0失败时返回错误码thread 线程对象attr 线程属性NULL代表默认属性routine 线程执行的函数arg 传递给routine的参数 参数是void * 注意传递参数格式编译错误分析1.createP_t.c:14:36: warning: passing argument 3 of ‘pthread_create’ from incompatible pointer type [-Wincompatible-pointer-types]ret pthread_create(tid,NULL,testThread,NULL);^In file included from createP_t.c:1:0:/usr/include/pthread.h:233:12: note: expected ‘void * (*)(void)’ but argument is of type ‘int * ()(char *)’意义表示pthread_create参数3的定义和实际代码不符合期望的是void * (*)(void) 实际的代码是int * ()(char)解决方法改为pthread_create(tid,NULL,(void)testThread,NULL);createP_t.c:(.text0x4b)对‘pthread_create’未定义的引用collect2: error: ld returned 1 exit status --------这个链接错误表示pthread_create这个函数没有实现解决方法编译时候加 -lpthread注意事项1. 主进程的退出它创建的线程也会退出。线程创建需要时间如果主进程马上退出那线程不能得到执行获取线程的id通过pthread_create函数的第一个参数通过在线程里面调用pthread_self函数线程间参数传递重点难点编译错误createP_t.c:8:34: warning: dereferencing ‘void *’ pointerprintf(“input arg%d\n”,(int)*arg);^createP_t.c:8:5: error: invalid use of void expressionprintf(“input arg%d\n”,(int)arg);错误原因是void类型指针不能直接用取值arg因为编译不知道数据类型。解决方法转换为指定的指针类型后再用取值 比如(int *)arg通过地址传递参数注意类型的转换值传递这时候编译器会告警需要程序员自己保证数据长度正确运行错误*** stack smashing detected ***: ./mthread_t terminated已放弃 (核心已转储)原因栈被破坏了数组越界线程的回收使用pthread_join 函数#include pthread.hint pthread_join(pthread_t thread, void **retval);注意pthread_join 是阻塞函数如果回收的线程没有结束则一直等待编译错误pjoin.c:13:5: error: unknown type name ‘pthead_t’pthead_t tid;错误类型未知的类型pthead_t错误可能1拼写错误2对应的头文件没有包含pjoin.c:18:12: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘void’ [-Wformat]printf(“thread ret%s\n”,retv);错误类型参数不匹配期望的是char * 但参数retv是void *解决在参数前面加强制类型转换charretv使用线程的分离两种方式1 使用pthread_detach2 创建线程时候设置为分离属性pthread_attr_t attr;pthread_attr_init(attr);pthread_attr_setdetachstate(attr,PTHREAD_CREATE_DETACHED);#includepthread.h#includestdio.h#includeunistd.h// 子线程函数void*func(void*arg){printf(This is child thread\n);while(1){sleep(5);// 这是一个「取消点」}pthread_exit(thread return);// 这行永远不会执行}intmain(){pthread_ttid;void*retv;inti;pthread_create(tid,NULL,func,NULL);// 创建子线程sleep(5);// 主线程等 5 秒让子线程先运行pthread_cancel(tid);// 发送取消请求给子线程pthread_join(tid,retv);// 等待子线程结束并获取退出状态printf(thread ret%s\n,(char*)retv);// 打印子线程的返回值while(1){// 主线程死循环保持程序运行}}核心结论主线程睡 5 秒 ≠ 子线程刚好睡完 5 秒它们是同时、并行在跑的不是等一个跑完再跑另一个我给你画一个绝对清晰的时间轴时间单位秒0 秒时刻主线程创建子线程子线程立刻开始运行打印一句话马上进入 sleep(5)主线程创建完线程立刻也进入 sleep(5)两个人同时开始睡觉1 秒子线程还在睡主线程还在睡2 秒都在睡3 秒都在睡4 秒都在睡5 秒 关键主线程睡醒了主线程立刻执行pthread_cancel(tid)发送取消信号而此时子线程的 sleep(5) 也刚好 5 秒到了准备醒来最关键的瞬间谁先动子线程在第 5 秒结束时要做的第一件事从 sleep(5) 中醒来 → 检查取消请求主线程在第 5 秒结束时做的事发送取消请求结果子线程刚要醒来就发现“哦有人取消我了”→ 直接在sleep(5)这个取消点被杀死。用一句人话总结主线程和子线程是同时睡觉的5 秒后一起醒。主线程一醒就发取消信号子线程一醒就检查信号直接被杀死。所以子线程根本没机会跳出 while 循环也没机会执行下一轮代码。你担心的误区是这样的错误理解❌ 错误时间线子线程先跑sleep(5)等子线程睡完 5 秒主线程才开始 sleep(5)主线程睡醒才取消这完全错了线程是并发的不是排队执行的再验证一下如果把代码改成这样子线程就不会被取消// 子线程while(1){// 没有任何取消点纯计算inta0;a;}这种情况下主线程取消也没用因为没有取消点可以检查信号。最终总结主线程和子线程是同时运行的它们同时开始 sleep(5)5 秒后同时醒来子线程刚从 sleep 醒来取消点就被主线程发的取消信号杀死所以子线程永远死在 sleep(5) 这里线程的取消意义随时杀掉一个线程int pthread_cancel(pthread_t thread);注意线程的取消要有取消点才可以不是说取消就取消线程的取消点主要是阻塞的系统调用运行段错误调试可以使用gdb调试使用gdb 运行代码gdb ./youapp(gdb) run等待出现Thread 1 “pcancel” received signal SIGSEGV, Segmentation fault.输入命令bt打印调用栈(gdb) bt#0 0x00007ffff783ecd0 in vfprintf () from /lib/x86_64-linux-gnu/libc.so.6#1 0x00007ffff78458a9 in printf () from /lib/x86_64-linux-gnu/libc.so.6#2 0x00000000004007f9 in main () at pcancel.c:21确定段错误位置是pcancel.c 21行如果没有取消点手动设置一个void pthread_testcancel(void);设置取消使能或禁止int pthread_setcancelstate(int state, int *oldstate);PTHREAD_CANCEL_ENABLEPTHREAD_CANCEL_DISABLE设置取消类型int pthread_setcanceltype(int type, int *oldtype);PTHREAD_CANCEL_DEFERRED 等到取消点才取消PTHREAD_CANCEL_ASYNCHRONOUS 目标线程会立即取消线程的清理必要性 当线程非正常终止需要清理一些资源。void pthread_cleanup_push(void (*routine) (void *), void *arg)void pthread_cleanup_pop(int execute)routine 函数被执行的条件被pthread_cancel取消掉。执行pthread_exit非0参数执行pthread_cleanup_pop()注意必须成对使用即使pthread_cleanup_pop不会被执行到也必须写上否则编译错误。2.pthread_cleanup_pop()被执行且参数为0pthread_cleanup_push回调函数routine不会被执行.3 pthread_cleanup_push 和pthread_cleanup_pop可以写多对routine执行顺序正好相反线程内的return 可以结束线程也可以给pthread_join返回值但不能触发pthread_cleanup_push里面的回调函数所以我们结束线程尽量使用pthread_exit退出线程。
7. 线程编程(线程概念和创建)
发布时间:2026/5/23 9:50:16
线程的创建#include pthread.hint pthread_create(pthread_t *thread, constpthread_attr_t *attr, void *(*routine)(void *), void *arg);成功返回0失败时返回错误码thread 线程对象attr 线程属性NULL代表默认属性routine 线程执行的函数arg 传递给routine的参数 参数是void * 注意传递参数格式编译错误分析1.createP_t.c:14:36: warning: passing argument 3 of ‘pthread_create’ from incompatible pointer type [-Wincompatible-pointer-types]ret pthread_create(tid,NULL,testThread,NULL);^In file included from createP_t.c:1:0:/usr/include/pthread.h:233:12: note: expected ‘void * (*)(void)’ but argument is of type ‘int * ()(char *)’意义表示pthread_create参数3的定义和实际代码不符合期望的是void * (*)(void) 实际的代码是int * ()(char)解决方法改为pthread_create(tid,NULL,(void)testThread,NULL);createP_t.c:(.text0x4b)对‘pthread_create’未定义的引用collect2: error: ld returned 1 exit status --------这个链接错误表示pthread_create这个函数没有实现解决方法编译时候加 -lpthread注意事项1. 主进程的退出它创建的线程也会退出。线程创建需要时间如果主进程马上退出那线程不能得到执行获取线程的id通过pthread_create函数的第一个参数通过在线程里面调用pthread_self函数线程间参数传递重点难点编译错误createP_t.c:8:34: warning: dereferencing ‘void *’ pointerprintf(“input arg%d\n”,(int)*arg);^createP_t.c:8:5: error: invalid use of void expressionprintf(“input arg%d\n”,(int)arg);错误原因是void类型指针不能直接用取值arg因为编译不知道数据类型。解决方法转换为指定的指针类型后再用取值 比如(int *)arg通过地址传递参数注意类型的转换值传递这时候编译器会告警需要程序员自己保证数据长度正确运行错误*** stack smashing detected ***: ./mthread_t terminated已放弃 (核心已转储)原因栈被破坏了数组越界线程的回收使用pthread_join 函数#include pthread.hint pthread_join(pthread_t thread, void **retval);注意pthread_join 是阻塞函数如果回收的线程没有结束则一直等待编译错误pjoin.c:13:5: error: unknown type name ‘pthead_t’pthead_t tid;错误类型未知的类型pthead_t错误可能1拼写错误2对应的头文件没有包含pjoin.c:18:12: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘void’ [-Wformat]printf(“thread ret%s\n”,retv);错误类型参数不匹配期望的是char * 但参数retv是void *解决在参数前面加强制类型转换charretv使用线程的分离两种方式1 使用pthread_detach2 创建线程时候设置为分离属性pthread_attr_t attr;pthread_attr_init(attr);pthread_attr_setdetachstate(attr,PTHREAD_CREATE_DETACHED);#includepthread.h#includestdio.h#includeunistd.h// 子线程函数void*func(void*arg){printf(This is child thread\n);while(1){sleep(5);// 这是一个「取消点」}pthread_exit(thread return);// 这行永远不会执行}intmain(){pthread_ttid;void*retv;inti;pthread_create(tid,NULL,func,NULL);// 创建子线程sleep(5);// 主线程等 5 秒让子线程先运行pthread_cancel(tid);// 发送取消请求给子线程pthread_join(tid,retv);// 等待子线程结束并获取退出状态printf(thread ret%s\n,(char*)retv);// 打印子线程的返回值while(1){// 主线程死循环保持程序运行}}核心结论主线程睡 5 秒 ≠ 子线程刚好睡完 5 秒它们是同时、并行在跑的不是等一个跑完再跑另一个我给你画一个绝对清晰的时间轴时间单位秒0 秒时刻主线程创建子线程子线程立刻开始运行打印一句话马上进入 sleep(5)主线程创建完线程立刻也进入 sleep(5)两个人同时开始睡觉1 秒子线程还在睡主线程还在睡2 秒都在睡3 秒都在睡4 秒都在睡5 秒 关键主线程睡醒了主线程立刻执行pthread_cancel(tid)发送取消信号而此时子线程的 sleep(5) 也刚好 5 秒到了准备醒来最关键的瞬间谁先动子线程在第 5 秒结束时要做的第一件事从 sleep(5) 中醒来 → 检查取消请求主线程在第 5 秒结束时做的事发送取消请求结果子线程刚要醒来就发现“哦有人取消我了”→ 直接在sleep(5)这个取消点被杀死。用一句人话总结主线程和子线程是同时睡觉的5 秒后一起醒。主线程一醒就发取消信号子线程一醒就检查信号直接被杀死。所以子线程根本没机会跳出 while 循环也没机会执行下一轮代码。你担心的误区是这样的错误理解❌ 错误时间线子线程先跑sleep(5)等子线程睡完 5 秒主线程才开始 sleep(5)主线程睡醒才取消这完全错了线程是并发的不是排队执行的再验证一下如果把代码改成这样子线程就不会被取消// 子线程while(1){// 没有任何取消点纯计算inta0;a;}这种情况下主线程取消也没用因为没有取消点可以检查信号。最终总结主线程和子线程是同时运行的它们同时开始 sleep(5)5 秒后同时醒来子线程刚从 sleep 醒来取消点就被主线程发的取消信号杀死所以子线程永远死在 sleep(5) 这里线程的取消意义随时杀掉一个线程int pthread_cancel(pthread_t thread);注意线程的取消要有取消点才可以不是说取消就取消线程的取消点主要是阻塞的系统调用运行段错误调试可以使用gdb调试使用gdb 运行代码gdb ./youapp(gdb) run等待出现Thread 1 “pcancel” received signal SIGSEGV, Segmentation fault.输入命令bt打印调用栈(gdb) bt#0 0x00007ffff783ecd0 in vfprintf () from /lib/x86_64-linux-gnu/libc.so.6#1 0x00007ffff78458a9 in printf () from /lib/x86_64-linux-gnu/libc.so.6#2 0x00000000004007f9 in main () at pcancel.c:21确定段错误位置是pcancel.c 21行如果没有取消点手动设置一个void pthread_testcancel(void);设置取消使能或禁止int pthread_setcancelstate(int state, int *oldstate);PTHREAD_CANCEL_ENABLEPTHREAD_CANCEL_DISABLE设置取消类型int pthread_setcanceltype(int type, int *oldtype);PTHREAD_CANCEL_DEFERRED 等到取消点才取消PTHREAD_CANCEL_ASYNCHRONOUS 目标线程会立即取消线程的清理必要性 当线程非正常终止需要清理一些资源。void pthread_cleanup_push(void (*routine) (void *), void *arg)void pthread_cleanup_pop(int execute)routine 函数被执行的条件被pthread_cancel取消掉。执行pthread_exit非0参数执行pthread_cleanup_pop()注意必须成对使用即使pthread_cleanup_pop不会被执行到也必须写上否则编译错误。2.pthread_cleanup_pop()被执行且参数为0pthread_cleanup_push回调函数routine不会被执行.3 pthread_cleanup_push 和pthread_cleanup_pop可以写多对routine执行顺序正好相反线程内的return 可以结束线程也可以给pthread_join返回值但不能触发pthread_cleanup_push里面的回调函数所以我们结束线程尽量使用pthread_exit退出线程。