• lv5 嵌入式开发-5 线程的创建和参数传递


    目录

    1 线程基础

    2 Linux线程库

    2.1 线程创建 – pthread_create

    2.2 线程结束 – pthread_exit

    2.3 线程查看tid函数

    2.4 线程间参数传递(重点)

    2.4.1 练习

    2.5 线程查看命令(多线程)

    2.6 线程回收 – pthread_join

    2.7 线程分离pthead_detach和设置属性

    2.7.1 pthead_detach方式

    2.7.1 设置Attr属性

    2.8 取消一个线程

    2.8.1 pthread_cancel

     2.8.2 pthread_testcancel

    2.8.3 pthread_setcancelstate

    2.8.4 pthread_setcanceltype

    2.9 线程的清理


    掌握:线程概念、线程特点、线程创建

    1 线程基础

    线程概念

    进程

    • 进程有独立的地址空间
    • Linux为每个进程创建task_struct
    • 每个进程都参与内核调度,互不影响

    线程

    • 进程在切换时系统开销大
    • 很多操作系统引入了轻量级进程LWP
    • 同一进程中的线程共享相同地址空间
    • Linux不区分进程、线程

    线程特点

    • 通常线程指的是共享相同地址空间的多个任务

    使用多线程的好处

    • 大大提高了任务切换的效率
    • 避免了额外的TLB & cache的刷新

    线程共享资源

    一个进程中的多个线程共享以下资源:

    • 可执行的指令
    • 静态数据(如果再一个进程里定义的全局变量,在进程里所有线程都能访问,进程A里定义的全局变量,在进程B里是不能访问的,包括子进程也不行
    • 进程中打开的文件描述符
    • 当前工作目录
    • 用户ID
    • 用户组ID

    线程私有资源

    每个线程私有的资源包括:

    • 线程ID (TID)
    • PC(程序计数器)和相关寄存器
    • 堆栈(局部变量)
    • 错误号 (errno)
    • 优先级
    • 执行状态和属性

    2 Linux线程库

    linux内核中并没有实现线程,所以通过线程库实现

    pthread线程库中提供了如下基本操作

    • 创建线程
    • 回收线程
    • 结束线程

    同步和互斥机制

    • 信号量
    • 互斥锁

    2.1 线程创建 – pthread_create

    1. #include
    2. int pthread_create(pthread_t *thread, const
    3. pthread_attr_t *attr, void *(*routine)(void *), void *arg);
    • 成功返回0,失败时返回错误码  
    • thread 线程对象  (实际是整数的指针)
    • attr 线程属性,NULL代表默认属性  
    • routine 线程执行的函数  (回调函数,*routine 是函数指针 输入参数和返回值都是void*)
    • arg 传递给routine的参数 ,参数是void * ,注意传递参数格式

    示例:

    1. #include
    2. #include
    3. #include
    4. int *testThread(char *arg){
    5. printf("This is a thread test\n");
    6. return NULL;
    7. }
    8. int main(){
    9. pthread_t tid;
    10. int ret;
    11. ret = pthread_create(&tid,NULL,(void *)testThread,NULL);
    12. printf("This is main thread\n");
    13. sleep(1);
    14. }
    15. //如果不加printf和sleep1,主线程结束,子线程也结束可能就打印不出内容。
    16. linux@linux:~/Desktop$ gcc -o pthread pthread.c -lpthread
    17. linux@linux:~/Desktop$ ./pthread
    18. linux@linux:~/Desktop$ ./pthread

    编译错误分析:

    1. createP_t.c:14:36: warning: passing argument 3 of ‘pthread_create’ from incompatible pointer type [-Wincompatible-pointer-types]
    2.      ret = pthread_create(&tid,NULL,testThread,NULL);
    3.                                     ^
    4. In file included from createP_t.c:1:0:
    5. /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);

    1. createP_t.c:(.text+0x4b):对‘pthread_create’未定义的引用
    2. collect2: error: ld returned 1 exit status   --------这个链接错误,

    表示pthread_create这个函数没有实现

    解决方法:编译时候加 -lpthread

    注意事项:1. 主进程的退出,它创建的线程也会退出。

    线程创建需要时间,如果主进程马上退出,那线程不能得到执行

    2.2 线程结束 – pthread_exit

    1. #include
    2. void pthread_exit(void *retval);
    • 结束当前线程  
    • retval可被其他线程通过pthread_join获取  
    • 线程私有资源被释放

    示例

    1. #include
    2. #include
    3. #include
    4. void *testThread(void *arg){
    5. printf("This is a thread test\n");
    6. pthread_exit(NULL);
    7. printf("after pthread exit\n"); //不会被打印,线程已经清理
    8. }
    9. int main(){
    10. pthread_t tid;
    11. int ret;
    12. int arg = 5;
    13. ret = pthread_create(&tid,NULL,testThread,(void *)arg);
    14. printf("This is main thread\n");
    15. sleep(1);
    16. }
    17. //运行结果
    18. linux@linux:~/Desktop$ gcc -o pthread pthread.c -lpthread
    19. linux@linux:~/Desktop$ ./pthread
    20. This is main thread
    21. This is a thread test

    2.3 线程查看tid函数

    1. pthread_t pthread_self(void) 查看自己的TID
    2. #include
    3. pthread_t pthread_self(void);

     示例:

    1. #include
    2. #include
    3. #include
    4. void *testThread(void *arg){
    5. printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
    6. // return NULL;
    7. pthread_exit(NULL);
    8. printf("after pthread exit\n");
    9. }
    10. int main(){
    11. pthread_t tid;
    12. int ret;
    13. ret = pthread_create(&tid,NULL,testThread,(void *)arg);
    14. printf("This is main thread,tid=%lu\n",tid);
    15. sleep(1);
    16. }

    2.4 线程间参数传递(重点)

    1. pthread_create(pthread_t *thread, const
    2. pthread_attr_t *attr, void *(*routine)(void *), void *arg);

    最后一个参数

    示例:

    1. //方式1
    2. //直接传参数值也可以,把数当地址来传入了。因为int和指针都是4字节,如果是long就不行了
    3. #include
    4. #include
    5. #include
    6. void *testThread(void *arg){
    7. printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
    8. // return NULL;
    9. printf("input arg=%d\n",(int)arg);
    10. pthread_exit(NULL);
    11. printf("after pthread exit\n");
    12. }
    13. int main(){
    14. pthread_t tid;
    15. int ret;
    16. int arg = 5;
    17. ret = pthread_create(&tid,NULL,testThread,(void *)arg);
    18. printf("This is main thread,tid=%lu\n",tid);
    19. sleep(1);
    20. }
    21. //方式2
    22. #include
    23. #include
    24. #include
    25. void *testThread(void *arg){
    26. printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
    27. // return NULL;
    28. printf("input arg=%d\n",*(int *)arg);
    29. pthread_exit(NULL);
    30. printf("after pthread exit\n");
    31. }
    32. int main(){
    33. pthread_t tid;
    34. int ret;
    35. int arg = 5;
    36. ret = pthread_create(&tid,NULL,testThread,(void *)&arg);
    37. printf("This is main thread,tid=%lu\n",tid);
    38. sleep(1);
    39. }

    补充:

    编译错误:

    1. createP_t.c:8:34: warning: dereferencing ‘void *’ pointer
    2.      printf("input arg=%d\n",(int)*arg);
    3.                                 ^
    4. createP_t.c:8:5: error: invalid use of void expression
    5.      printf("input arg=%d\n",(int)*arg);

    错误原因是void *类型指针不能直接用*取值(*arg),因为编译不知道数据类型。

    解决方法:转换为指定的指针类型后再用*取值  比如:*(int *)arg

    1. 通过地址传递参数,注意类型的转换
    2. 值传递,这时候编译器会告警,需要程序员自己保证数据长度正确

    示例:

    1. #include
    2. #include
    3. #include
    4. void *testThread(void *arg){
    5. printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
    6. // return NULL;
    7. printf("This is %d thread.\n", (int)arg);
    8. // pthread_exit(NULL);
    9. while(1){
    10. sleep(1);
    11. }
    12. printf("after pthread exit\n");
    13. }
    14. int main(){
    15. pthread_t tid[5];
    16. int ret;
    17. int arg = 5;
    18. int i;
    19. for(i=0;i<5;i++){
    20. ret = pthread_create(&tid[i],NULL,testThread,(void *)i);
    21. // sleep(1); //执行效率低无法打印i的值,因为传入的是地址,还没来得及改变,可以改用传值
    22. printf("This is main thread,tid=%lu\n",tid[i]);
    23. }
    24. while(1){
    25. sleep(1);
    26. }
    27. }

    运行错误:

    *** stack smashing detected ***: ./mthread_t terminated

    已放弃 (核心已转储)

    原因:栈被破坏了(数组越界)

    2.4.1 练习

    使用pthread_create实现 10 个子线程,并且让每个子线程打印自己的线程号

    1. #include
    2. #include
    3. #include
    4. int *Thread_fun(char *arg)
    5. {
    6. printf("This is a thread test.pid=%d,tid=%lu,\n",getpid(),pthread_self());
    7. printf("This is %d thread.\n",(int)arg);
    8. while(1)
    9. {
    10. sleep(1);
    11. }
    12. printf("after pthread exit\n");
    13. return 0;
    14. }
    15. int main(int argc, char * argv[])
    16. {
    17. int i;
    18. pthread_t tid[10];
    19. int ret = 0;
    20. for(i = 0; i < 10; i++)
    21. {
    22. ret = pthread_create(&tid[i],NULL,(void *)Thread_fun,(void *)i);
    23. printf("This i main thread,tid=%lu\n",tid[i]);
    24. }
    25. while(1)
    26. {
    27. sleep(1);
    28. }
    29. }

    2.5 线程查看命令(多线程)

    ps -eLf
    

     示例

    2.6 线程回收 – pthread_join

    1. #include
    2. int pthread_join(pthread_t thread, void **retval);

    对于一个默认属性的线程 A 来说,线程占用的资源并不会因为执行结束而得到释放

    • 成功返回0,失败时返回错误码  
    • thread 要回收的线程对象(ID号)  
    • 调用线程阻塞直到thread结束  
    • *retval 接收线程thread的返回值

    注意:pthread_join 是阻塞函数,如果回收的线程没有结束,则一直等待

    示例

    1. #include
    2. #include
    3. #include
    4. void *Thread_fun(void *arg)
    5. {
    6. printf("This is a child thread\n");
    7. sleep(1);
    8. pthread_exit("thread return");
    9. }
    10. int main(int argc, char * argv[])
    11. {
    12. pthread_t tid;
    13. void *retv;
    14. pthread_create(&tid,NULL,Thread_fun,NULL);
    15. pthread_join(tid,&retv);
    16. printf("thread ret=%s\n",(char *)retv);
    17. sleep(1);
    18. }
    19. //运行结果
    20. linux@linux:~/Desktop$ ./test_pthread
    21. This is a child thread
    22. thread ret=thread return

    多个线程回收 

    1. #include
    2. #include
    3. #include
    4. void *func(void *arg){
    5. printf("This is child thread\n");
    6. sleep(25);
    7. pthread_exit("thread return");
    8. }
    9. int main(){
    10. pthread_t tid[100];
    11. void *retv;
    12. int i;
    13. for(i=0;i<100;i++){
    14. pthread_create(&tid[i],NULL,func,NULL);
    15. }
    16. for(i=0;i<100;i++){
    17. pthread_join(tid[i],&retv);
    18. printf("thread ret=%s\n",(char*)retv);
    19. }
    20. while(1){
    21. sleep(1);
    22. }
    23. }

    回收效果,使用top命令

    1. linux@linux:~$ ps -ef|grep "pjoin"
    2. linux 16330 2072 0 10:46 ? 00:00:01 gedit /home/linux/Desktop/pjoin.c
    3. linux 16467 2732 0 10:50 pts/9 00:00:00 ./pjoin
    4. linux 16573 15358 0 10:51 pts/1 00:00:00 grep --color=auto pjoin
    5. linux@linux:~$ top -p 16467

    发现回收前后,虚拟内存减小,实际内存减小,如果不使用回收,内存不会有变化还可能变大。

    2.7 线程分离pthead_detach和设置属性

    使用线程的分离两种方式:

    1 使用pthread_detach

    2 创建线程时候设置为分离属性

    2.7.1 pthead_detach方式

    int pthread_detach(pthread_t thread);    

    成功:0;失败:错误号
    指定该状态,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程

    此种方式不需要通过主线程取回收线程资源。
    示例

    1. #include
    2. #include
    3. #include
    4. void *func(void *arg){
    5. pthread_detach(pthread_self()); //方式2
    6. printf("This is child thread\n");
    7. sleep(25);
    8. pthread_exit("thread return");
    9. }
    10. int main(){
    11. pthread_t tid[100];
    12. void *retv;
    13. int i;
    14. for(i=0;i<100;i++){
    15. pthread_create(&tid[i],NULL,func,NULL);
    16. //pthread_detach(tid); //方式1
    17. }
    18. while(1){
    19. sleep(1);
    20. }
    21. }

     实际效果:同2.6回收效果,虚拟内存减小,实际内存减小,如果不使用detach,内存不会有变化还可能变大。

    2.7.1 设置Attr属性

     pthread_attr_t attr;   /*通过线程属性来设置游离态(分离态)*/
    

    设置线程属性为分离

    1. pthread_attr_init(&attr);
    2. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    示例:

    1. #include
    2. #include
    3. #include
    4. void *func(void *arg){
    5. printf("This is child thread\n");
    6. sleep(25);
    7. pthread_exit("thread return");
    8. }
    9. int main(){
    10. pthread_t tid[100];
    11. void *retv;
    12. int i;
    13. pthread_attr_t attr;
    14. pthread_attr_init(&attr);
    15. pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); //分离属性
    16. for(i=0;i<100;i++){
    17. pthread_create(&tid[i],&attr,func,NULL);
    18. // pthread_detach(tid);
    19. }
    20. while(1){
    21. sleep(1);
    22. }
    23. }

    线程分离的目的: 

    1. 资源回收:当线程被标记为可分离(detached)时,线程退出后,系统会自动回收其占用的资源,无需其他线程等待或执行特定的回收操作。这对于长时间运行或创建大量线程的应用程序很有用,因为它可以减少资源泄漏的风险。

    2. 线程管理:标记线程为可分离使得其独立于主线程或其他线程而存在,它可以自主地运行和结束,不会影响其他线程的正常执行。这对于一些需要并发执行的任务或周期性任务非常有用,可以提高整体的系统性能和响应能力。

    需要注意的是,线程创建时的默认属性是非分离(joinable),即需要使用 pthread_join 函数来等待线程的结束并回收资源。如果需要将线程设置为可分离属性,可以使用 pthread_attr_setdetachstate 函数将属性设置为 PTHREAD_CREATE_DETACHED

    2.8 取消一个线程

    2.8.1 pthread_cancel

    int pthread_cancel(pthread_t thread);     杀死一个线程
    

    作用:如果一个线程是个死循环,那么exit可能永远也执行不到。这种情况需要取消线程的功能。

    意义:随时杀掉一个线程,如果使用kill命令,会把进程也一起杀掉。

    注意:线程的取消要有取消点才可以,不是说取消就取消,线程的取消点主要是阻塞的系统调用

    示例:

    1. #include
    2. #include
    3. #include
    4. void *func(void *arg){
    5. printf("This is child thread\n");
    6. while(1)
    7. {
    8. sleep(5);
    9. }
    10. pthread_exit("thread return"); //永远不会执行
    11. }
    12. int main(){
    13. pthread_t tid;
    14. void *retv;
    15. int i;
    16. pthread_create(&tid,NULL,func,NULL);
    17. sleep(5);
    18. pthread_cancel(tid);
    19. pthread_join(tid,&retv);
    20. printf("thread ret=%s\n",(char*)retv); //这里会出现段错误,这是空指针
    21. while(1){
    22. sleep(1);
    23. }
    24. }

    运行段错误调试:

    可以使用gdb调试

    使用gdb 运行代码,gdb  ./youapp

    1. (gdb) run
    2. 等待出现Thread 1 "pcancel" received signal SIGSEGV, Segmentation fault.
    3. 输入命令bt(打印调用栈)
    4. (gdb) bt
    5. #0  0x00007ffff783ecd0 in vfprintf () from /lib/x86_64-linux-gnu/libc.so.6 //vfprintf报错
    6. #1  0x00007ffff78458a9 in printf () from /lib/x86_64-linux-gnu/libc.so.6 //#2调用了#1
    7. #2  0x00000000004007f9 in main () at pcancel.c:21 //栈底
    8. 确定段错误位置是pcancel.c 21

    注释掉后调试结果

    1. linux@linux:~$ ps -eLf|grep "pcancel"
    2. linux 3467 2962 3467 0 2 02:50 pts/7 00:00:00 ./pcancel
    3. linux 3467 2962 3468 0 2 02:50 pts/7 00:00:00 ./pcancel
    4. linux 3470 3184 3470 0 1 02:50 pts/2 00:00:00 grep --color=auto pcancel
    5. linux@linux:~$ ps -eLf|grep "pcancel"
    6. linux 3467 2962 3467 0 2 02:50 pts/7 00:00:00 ./pcancel
    7. linux 3467 2962 3468 0 2 02:50 pts/7 00:00:00 ./pcancel
    8. linux 3472 3184 3472 0 1 02:50 pts/2 00:00:00 grep --color=auto pcancel
    9. linux@linux:~$ ps -eLf|grep "pcancel"
    10. linux 3467 2962 3467 0 1 02:50 pts/7 00:00:00 ./pcancel
    11. linux 3474 3184 3474 0 1 02:50 pts/2 00:00:00 grep --color=auto pcancel
    12. linux@linux:~$

     2.8.2 pthread_testcancel

    如果没有取消点,手动设置一个

    void pthread_testcancel(void);
    1. #include
    2. #include
    3. #include
    4. void *func(void *arg){
    5. printf("This is child thread\n");
    6. while(1)
    7. {
    8. pthread_testcancel(); //如果程序代码很长,找不到死循环的位置,可以用这段代码
    9. }
    10. pthread_exit("thread return"); //永远不会执行
    11. }
    12. int main(){
    13. pthread_t tid;
    14. void *retv;
    15. int i;
    16. pthread_create(&tid,NULL,func,NULL);
    17. sleep(5);
    18. pthread_cancel(tid);
    19. while(1){
    20. sleep(1);
    21. }
    22. }

    如果没有取消点,手动设置一个

    2.8.3 pthread_setcancelstate

    目的:让有些代码可以被取消,有些代码不能被取消。有些先后顺序不好找的时候。

    1. int pthread_setcancelstate(int state, int *oldstate);
    2. PTHREAD_CANCEL_ENABLE
    3. PTHREAD_CANCEL_DISABLE
    1. #include
    2. #include
    3. #include
    4. void *func(void *arg){
    5. printf("This is child thread\n");
    6. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); //不能被取消,不管有没有阻塞点
    7. while(1)
    8. {
    9. sleep(5);
    10. pthread_testcancel();
    11. }
    12. pthread_exit("thread return"); //永远不会执行
    13. }
    14. int main(){
    15. pthread_t tid;
    16. void *retv;
    17. int i;
    18. pthread_create(&tid,NULL,func,NULL);
    19. sleep(1);
    20. pthread_cancel(tid);
    21. pthread_join(tid,&retv);
    22. // printf("thread ret=%s\n",(char*)retv);
    23. while(1){
    24. sleep(1);
    25. }
    26. }
    27. //运行结果
    28. linux@linux:~$ ps -eLf|grep "pcancel"
    29. linux 6271 2962 6271 0 2 03:43 pts/7 00:00:00 ./pcancel
    30. linux 6271 2962 6272 0 2 03:43 pts/7 00:00:00 ./pcancel
    31. linux 6287 3184 6287 0 1 03:46 pts/2 00:00:00 grep --color=auto pcancel

    此时在设置可以取消。

    1. #include
    2. #include
    3. #include
    4. void *func(void *arg){
    5. printf("This is child thread\n");
    6. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    7. sleep(5);
    8. pthread_testcancel(); //如果程序代码很长,找不到死循环的位置,可以用这段代码
    9. pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); //可以取消了
    10. pthread_exit("thread return"); //永远不会执行
    11. }
    12. int main(){
    13. pthread_t tid;
    14. void *retv;
    15. int i;
    16. pthread_create(&tid,NULL,func,NULL);
    17. sleep(5); //5秒内不能被取消,可以执行完上述代码
    18. pthread_cancel(tid);
    19. while(1){
    20. sleep(1);
    21. }
    22. }

    总结:

    能不能取消却决于有没有取消点,pthread_testcancel(),同时设置是否能被取消。

    如果不能被取消,则有没有取消点都没有关系了。

    2.8.4 pthread_setcanceltype

    1. int pthread_setcanceltype(int type, int *oldtype);
    2. PTHREAD_CANCEL_DEFERRED       等到取消点才取消         
    3. PTHREAD_CANCEL_ASYNCHRONOUS   目标线程会立刻取消

    2.9 线程的清理

    问题:如果线程取消了,代码没有正常退出,内存没有释放,会造成内存泄漏怎么解决?

    必要性:当线程非正常终止,需要清理一些资源。

    1. void pthread_cleanup_push(void (*routine) (void *), void *arg)
    2. void pthread_cleanup_pop(int execute)

    两个函数需要成对出现再代码中,否则会出错:

    1. #include
    2. #include
    3. void cleanup(void *arg)
    4. {
    5. printf("cleanup=%s\n",(char*)arg);
    6. }
    7. void *func(void *arg)
    8. {
    9. printf("This is child thread\n");
    10. pthread_cleanup_push(cleanup,"abcd");
    11. pthread_exit("thread return");
    12. }
    13. int main()
    14. {
    15. pthread_t tid;
    16. void *retv;
    17. int i;
    18. pthread_create(&tid,NULL,func,NULL);
    19. sleep(1);
    20. pthread_cancel(tid);
    21. pthread_join(tid,&revt);
    22. while(1)
    23. {
    24. sleep(1);
    25. }
    26. }
    27. linux@linux:~/Desktop$ gcc -g -o pcancel pcancel.c -lpthread
    28. pcancel.c: In function ‘func’:
    29. pcancel.c:18:1: error: expected ‘while’ before ‘int
    30. int main()
    31. ^
    32. pcancel.c:33:1: error: expected declaration or statement at end of input
    33. }
    34. ^
    35. pcancel.c:33:1: error: expected declaration or statement at end of input

    查看vim /usr/include/pthread.h 

    正确用法:

    1. #include
    2. #include
    3. #include
    4. void cleanup(void *arg)
    5. {
    6. //做清理工作的代码
    7. printf("cleanup=%s\n",(char*)arg);
    8. }
    9. void *func(void *arg)
    10. {
    11. printf("This is child thread\n");
    12. pthread_cleanup_push(cleanup,"abcd");
    13. pthread_exit("thread return"); //执行这句话,上面cleanup也会被调用到。
    14. pthread_cleanup_pop(0); //此时这句话意义在于完成大括号结束,0代表删除函数
    15. }
    16. /*
    17. void *func(void *arg)
    18. {
    19. printf("This is child thread\n");
    20. pthread_cleanup_push(cleanup,"abcd");
    21. pthread_cleanup_pop(1); // 非0参数执行pthread_cleanup_pop()
    22. pthread_exit("thread return");
    23. }
    24. */
    25. int main()
    26. {
    27. pthread_t tid;
    28. void *retv;
    29. int i;
    30. pthread_create(&tid,NULL,func,NULL);
    31. sleep(1);
    32. pthread_cancel(tid);
    33. pthread_join(tid,&revt);
    34. while(1)
    35. {
    36. sleep(1);
    37. }
    38. }
    39. //运行结果
    40. linux@linux:~/Desktop$ ./pcancel
    41. This is child thread
    42. cleanup=abcd

    routine 函数被执行的条件:

    1. 被pthread_cancel取消掉。
    2. 执行pthread_exit
    3. 非0参数执行pthread_cleanup_pop()

    注意:

    • 必须成对使用,即使pthread_cleanup_pop不会被执行到也必须写上,否则编译错误。
    • pthread_cleanup_pop()被执行且参数为0,pthread_cleanup_push回调函数routine不会被执行.
    • pthread_cleanup_push 和pthread_cleanup_pop可以写多对,routine执行顺序正好相反(先入后出)
    • 线程内的return 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程。
    1. #include
    2. #include
    3. #include
    4. void cleanup(void *arg){
    5. printf("cleanup,arg=%s\n",(char*)arg);
    6. }
    7. void cleanup2(void* arg){
    8. printf("cleanup2,arg=%s\n",(char*)arg);
    9. }
    10. void *func(void *arg){
    11. printf("This is child thread\n");
    12. pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); //设置了遇到取消点立刻取消线程
    13. pthread_cleanup_push(cleanup,"abcd");
    14. pthread_cleanup_push(cleanup2,"efgh");
    15. //while(1)
    16. {
    17. sleep(1);
    18. }
    19. pthread_cancel(pthread_self()); //取消线程,也会触发回调
    20. printf("Should not print\n");
    21. while(1){
    22. printf("sleep\n");
    23. sleep(1);
    24. }
    25. pthread_exit("thread return");
    26. pthread_cleanup_pop(1);
    27. pthread_cleanup_pop(1);
    28. sleep(10);
    29. pthread_exit("thread return");
    30. }
    31. int main(){
    32. pthread_t tid;
    33. void *retv;
    34. int i;
    35. pthread_create(&tid,NULL,func,NULL);
    36. sleep(1);
    37. // pthread_cancel(tid);
    38. pthread_join(tid,&retv);
    39. //printf("thread ret=%s\n",(char*)retv);
    40. while(1){
    41. sleep(1);
    42. }
    43. }
    44. //运行结果
    45. linux@linux:~/Desktop$ ./pcancel
    46. This is child thread
    47. cleanup2,arg=efgh
    48. cleanup,arg=abcd

    如改变一下:return和 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程。

    1. #include
    2. #include
    3. #include
    4. void cleanup(void *arg){
    5. printf("cleanup,arg=%s\n",(char*)arg);
    6. }
    7. void cleanup2(void* arg){
    8. printf("cleanup2,arg=%s\n",(char*)arg);
    9. }
    10. void *func(void *arg){
    11. printf("This is child thread\n");
    12. pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    13. pthread_cleanup_push(cleanup,"abcd");
    14. pthread_cleanup_push(cleanup2,"efgh");
    15. //while(1)
    16. {
    17. sleep(1);
    18. }
    19. // pthread_cancel(pthread_self());
    20. //printf("Should not print\n");
    21. return "1234";
    22. while(1){
    23. printf("sleep\n");
    24. sleep(1);
    25. }
    26. pthread_exit("thread return");
    27. pthread_cleanup_pop(1);
    28. pthread_cleanup_pop(1);
    29. sleep(10);
    30. pthread_exit("thread return");
    31. }
    32. int main(){
    33. pthread_t tid;
    34. void *retv;
    35. int i;
    36. pthread_create(&tid,NULL,func,NULL);
    37. sleep(1);
    38. // pthread_cancel(tid);
    39. pthread_join(tid,&retv);
    40. printf("thread ret=%s\n",(char*)retv);
    41. while(1){
    42. sleep(1);
    43. }
    44. }

    练习

    void pthread_cleanup_push(void (*routine) (void *), void *arg)
    void pthread_cleanup_pop(int execute) 的本质是什么?D
    A. 两个函数
    B. pthread_cleanup_push函数可以取消一个线程
    C. pthread_cleanup_pop函数可以清理一个线程
    D. 两个宏定义,必须配对使用

  • 相关阅读:
    Design Pattern —— 创建型 —— 单例模式(上) ——概念特点、面试常问、具体实践案例、源码解读
    MacOS装载APFS移动硬盘出现49180错误
    MATLAB算法实战应用案例精讲-【图像处理】3D点云(附实战案例)
    Leetcode 344:反转字符串(双指针法)
    ++和*(解引用)的优先级
    HDLBits-Rule110
    c语言:解决判断两个字符串是不是互为旋转字符的问题
    文字识别的
    Web APIs Web APIs第二天
    常见面试题-Redis专栏(一)
  • 原文地址:https://blog.csdn.net/m0_60718520/article/details/133151515