【多线程编程学习笔记6】终止线程执行,千万别踩这个坑!
申明:本学习笔记是在该教程的基础上结合自己的学习情况进行的总结,不是原创,想要看原版的请看C语言中文网的多线程编程(C语言+Linux),该网站有很多好的编程学习教程,尤其是关于C语言的。
在《终止线程执行(3种方法)》一节中,我们对 pthread_cancel() 函数的功能和用法做了详细的介绍。总的来说,通过调用 pthread_cancel() 函数,一个线程可以向同进程内的另一个线程发送“终止执行”的信号(Cancel 信号),使目标线程结束执行。
实际使用 pthread_cancel() 函数时,很多读者会发现“Cancel 信号成功发送,但目标线程并未立即终止执行”等类似的问题举个例子,在 Linux 环境中执行如下程序:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h> //调用 sleep() 函数
void * thread_Fun(void * arg) {printf("新建线程开始执行\n");//插入无限循环的代码,测试 pthread_cancel()函数的有效性while(1);
}
int main()
{pthread_t myThread;void * mess;int value;int res;res = pthread_create(&myThread, NULL, thread_Fun, NULL);if (res != 0) {printf("线程创建失败\n");return 0;}sleep(1);//令 myThread 线程终止执行res = pthread_cancel(myThread);if (res != 0) {printf("终止 myThread 线程失败\n");return 0;}printf("等待 myThread 线程执行结束:\n");res = pthread_join(myThread, &mess);if (res != 0) {printf("等待线程失败\n");return 0;}if (mess == PTHREAD_CANCELED) {printf("myThread 线程被强制终止\n");}else {printf("error\n");}return 0;
}
程序中,主线程( main() 函数)试图调用 pthread_cancel() 函数终止 myThread 线程执行。从运行结果不难发现,pthread_cancel() 函数成功发送了 Cancel 信号,但目标线程仍在执行。
也就是说,接收到 Cancel 信号的目标线程并没有立即处理该信号,或者说目标线程根本没有理会此信号。解决类似的问题,我们就需要搞清楚目标线程对 Cancel 信号的处理机制。
根据上节的内容,pthread_join会阻塞调用它的线程,因此程序在执行完printf(“等待 myThread 线程执行结束:\n”);后就会一直阻塞在res = pthread_join(myThread, &mess);等待目标线程myThread执行完毕
线程对Cancel信号的处理
对于默认属性的线程,当有线程借助 pthread_cancel() 函数向它发送 Cancel 信号时,它并不会立即结束执行,而是选择在一个适当的时机结束执行。
所谓适当的时机,POSIX 标准中规定,当线程执行一些特殊的函数时,会响应 Cancel 信号并终止执行,比如常见的 pthread_join()、pthread_testcancel()、sleep()、system() 等,POSIX 标准称此类函数为“cancellation points”(中文可译为“取消点”)。
POSIX 标准中明确列举了所有可以作为取消点的函数,这里不再一一罗列,感兴趣的读者可以自行查阅 POSIX 标准手册。
此外,<pthread.h> 头文件还提供有 pthread_setcancelstate() 和 pthread_setcanceltype() 这两个函数,我们可以手动修改目标线程处理 Cancel 信号的方式。
1、pthread_setcancelstate()函数
借助 pthread_setcancelstate() 函数,我们可以令目标线程处理 Cancal 信号,也可以令目标线程不理会其它线程发来的 Cancel 信号。
pthread_setcancelstate() 函数的语法格式如下:
int pthread_setcancelstate( int state , int * oldstate );
- state 参数有两个可选值,分别是:
- PTHREAD_CANCEL_ENABLE(默认值):当前线程会处理其它线程发送的 Cancel 信号;
- PTHREAD_CANCEL_DISABLE:当前线程不理会其它线程发送的 Cancel 信号,直到线程状态重新调整为 PTHREAD_CANCEL_ENABLE 后,才处理接收到的 Cancel 信号。
- oldtate 参数用于接收线程先前所遵循的 state 值,通常用于对线程进行重置。如果不需要接收此参数的值,置为 NULL 即可。
pthread_setcancelstate() 函数执行成功时,返回数字 0,反之返回非零数。
2、pthread_setcanceltype()函数
当线程会对 Cancel 信号进行处理时,我们可以借助 pthread_setcanceltype() 函数设置线程响应 Cancel 信号的时机。
pthread_setcanceltype() 函数的语法格式如下:
int pthread_setcanceltype( int type , int * oldtype );
- type 参数有两个可选值,分别是:
- PTHREAD_CANCEL_DEFERRED(默认值):当线程执行到某个可作为取消点的函数时终止执行;
- PTHREAD_CANCEL_ASYNCHRONOUS:线程接收到 Cancel 信号后立即结束执行。
- oldtype 参数用于接收线程先前所遵循的 type 值,如果不需要接收该值,置为 NULL 即可。
pthread_setcanceltype() 函数执行成功时,返回数字 0,反之返回非零数。
接下来通过一个实例给大家演示以上两个函数的功能和用法:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h> //调用 sleep() 函数void * thread_Fun(void * arg) {printf("新建线程开始执行\n");int res;//设置线程为可取消状态res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);if (res != 0) {printf("修改线程可取消状态失败\n");return NULL;}//设置线程接收到 Cancel 信号后立即结束执行res = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);if (res != 0) {printf("修改线程响应 Cancel 信号的方式失败\n");return NULL;}while (1);return NULL;
}
int main()
{pthread_t myThread;void * mess;int value;int res;res = pthread_create(&myThread, NULL, thread_Fun, NULL);if (res != 0) {printf("线程创建失败\n");return 0;}sleep(1);//向 myThread 线程发送 Cancel 信号res = pthread_cancel(myThread);if (res != 0) {printf("终止 myThread 线程失败\n");return 0;}//等待 myThread 线程执行结束,获取返回值res = pthread_join(myThread, &mess);if (res != 0) {printf("等待线程失败\n");return 0;}if (mess == PTHREAD_CANCELED) {printf("myThread 线程被强制终止\n");}else {printf("error\n");}return 0;
}
假设程序编写在 thread.c 文件中,程序执行过程如下:
[root@localhost ~]# gcc thread.c -o thread.exe -lpthread
[root@localhost ~]# ./thread.exe
新建线程开始执行
myThread 线程被强制终止
上面是在linux上的执行结果,在Windows下的执行结果如下图
暂时还不知道是因为啥导致了这种结果,推测是因为运行环境不同,这边就先挖个坑,等后面了解了再来解决。
和《终止线程执行(3种方法)》一节中 pthread_cancel() 函数的演示程序相比,我们仅仅是将 myThread 线程设置为“接收到 Cancel 信号后立即结束执行”。通过对比两个程序的输出结果,很容易就可以体会出 pthread_setcancelstate() 和 pthread_setcanceltype() 函数的功能。
【多线程编程学习笔记6】终止线程执行,千万别踩这个坑!相关推荐
- 多线程编程学习笔记——线程池(二)
接上文 多线程编程学习笔记--线程池(一) 三.线程池与并行度 此示例是学习如何应用线程池实现大量的操作,及与创建大量线程进行工作的区别. 1. 代码如下 using System; using Sy ...
- 多线程编程学习笔记——async和await(三)
接上文 多线程编程学习笔记--async和await(一) 接上文 多线程编程学习笔记--async和await(二) 五. 处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...
- 多线程编程学习笔记——任务并行库(二)
接上文 多线程编程学习笔记--任务并行库(一) 三. 组合任务 本示例是学习如何设置相互依赖的任务.我们学习如何创建一个任务的子任务,这个子任务必须在父任务执行结束之后,再执行. 1,示例代码如下 ...
- Linux与C++11多线程编程(学习笔记)
多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...
- 多线程编程学习笔记——使用并发集合(三)
接上文 多线程编程学习笔记--使用并发集合(一) 接上文 多线程编程学习笔记--使用并发集合(二) 四. 使用ConcurrentBag创建一个可扩展的爬虫 本示例在多个独立的即可生产任务又可消费 ...
- 多线程编程学习笔记——任务并行库(三)
接上文 多线程编程学习笔记--任务并行库(一) 接上文 多线程编程学习笔记--任务并行库(二) 六. 实现取消选项 本示例学习如何实现基于Task的异步操作进行取消流程,以及在任务真正运行前如何知 ...
- python3多线程编程_Python 3多线程编程学习笔记-基础篇
本文是学习<Python核心编程>的学习笔记,介绍了Python中的全局解释器锁和常用的两个线程模块:thread, threading,并对比他们的优缺点和给出简单的列子. 全局解释器锁 ...
- Posix多线程编程学习笔记(二)—线程属性(3)
六.线程的作用域 函数pthread_attr_setscope和pthread_attr_getscope分别用来设置和得到线程的作用域,这两个函数的定义如下: 7. 名称:: pthread_at ...
- 多线程编程学习笔记1时间
时间 c语言如何处理时间 c语言如何处理时间:time.h long t0 = time(NULL) ;//获取unix时间(从1970年1月1日到当前时经过的秒数 sleep(3)://让程序休眠3 ...
最新文章
- powerdesigner 概念模型_“使用满足”分析框架下社交媒体用户持续使用行为的概念模型研究...
- 170828、Eclipse Java注释模板设置详解以及版权声明
- java.lang.UnsatisfiedLinkError: com.jacob.com.D...
- Android WebView 在内部打开链接,捕获错误
- java spr_Java中的42行代码中的URL缩短器服务(Java(?!)Spring Boot + Redis
- fanuc机器人刷机教程_发那科机器人的正确操作方法及步骤
- 如何在ROS环境中解码.bag格式数据
- C++ 泛型编程-模板
- 我的世界服务器如何开无限小号,实操神技能,微信能“无限”开小号?
- 确定空间直线延长线上的一点
- 前端食堂技术周刊第 40 期:HTTP/3、WebContainers 登陆 Firefox、Remix Conf 2022、VueConf US 2022
- 武大三行情书第一名---《螃蟹在剥我的壳》
- php携程源码,Swoole2.0协程的使用和源码解读
- @所有人:产品汪、运营喵专属台历,你值得拥有!
- 液晶如何显示变量的内容
- 听说你想进大厂?当心这13个MySQL送命题!
- Laravel 5.4设置logout注销账户的重定向路径
- vim 查找替换操作命令
- 一对一 视频聊天源码,不要小瞧社交平台的盈利方式
- ibatis遍历数组出错