pthread之如何正确的终止正在运行的子线程
最近开发一些东西,线程数非常之多,当用户输入Ctrl+C的情形下,默认的信号处理会把程序退出,这时有可能会有很多线程的资源没有得到很好的释放,造成了内存泄露等等诸如此类的问题,本文就是围绕着这么一个使用场景讨论如何正确的终止正在运行的子线程。其实本文更确切的说是解决 如何从待终止线程外部安全的终止正在运行的线程
首先我们来看一下,让当前正在运行的子线程停止的所有方法
1.任何一个线程调用exit
2.pthread_exit
3.pthread_kill
4.pthread_cancel
下面我们一一分析各种终止正在运行的程序的方法
任何一个线程调用exit
任何一个线程只要调用了exit都会导致进程结束,各种子线程当然也能很好的结束了,可是这种退出会有一个资源释放的问题.我们知道当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。没错,标准C++ IO流也会很好的在exit退出时得到flush并且释放资源,这些东西并不会造成资源的浪费(系统调用main函数入口类似于exit(main(argc,argv)))。这种结束所有线程(包括主线程)的方式实际上在很多时候是非常可取的,但是对于针对关闭时进行一些别的逻辑的处理(指非资源释放逻辑)就不会很好,例如我想在程序被kill掉之前统计一下完成了多少的工作,这个统计类似于MapReduce,需要去每个线程获取,并且最后归并程一个统一的结果等等场景)
pthread_exit
此函数的使用场景是当前运行的线程运行pthread_exit得到退出,对于各个子线程能够清楚地知道自己在什么时候结束的情景下,非常好用,可是实际上往往很多时候一个线程不能知道知道在什么时候该结束,例如遭遇Ctrl+C时,kill进程时,当然如果排除所有的外界干扰的话,那就让每个线程干完自己的事情后,然后自觉地乖乖的调用pthread_exit就可以了,这并不是本文需要讨论的内容,本文的情景就是讨论如何处理特殊情况。
这里还有一种方法,既然子线程可以通过pthread_exit来正确退出,那么我们可以在遭遇Ctrl+C时,kill进程时处理signal信号,然后分别给在某一个线程可以访问的公共区域存上一个flag变量,线程内部每运行一段时间(很短)来检查一下flag,若发现需要终止自己时,自己调用pthread_exit,此法有一个弱点就是当子线程需要进行阻塞的操作时,可能无暇顾及检查flag,例如socket阻塞操作。如果你的子线程的任务基本没有非阻塞的函数,那么这么干也不失为一种很好的方案。
pthread_kill
不要被这个可怕的邪恶的名字所吓倒,其实pthread_kill并不像他的名字那样威力大,使用之后,你会感觉,他徒有虚名而已
pthread_kill的职责其实只是向指定的线程发送signal信号而已,并没有真正的kill掉一个线程,当然这里需要说明一下,有些信号的默认行为就是exit,那此时你使用pthread_kill发送信号给目标线程,目标线程会根据这个信号的默认行为进行操作,有可能是exit。当然我们同时也可以更改获取某个信号的行为,以此来达到我们终止子线程的目的。
- #define _MULTI_THREADED
- #include <pthread.h>
- #include <stdio.h>
- #include <signal.h>
- #include "check.h"
- #define NUMTHREADS 3
- void sighand(int signo);
- void *threadfunc(void *parm)
- {
- pthread_t self = pthread_self();
- pthread_id_np_t tid;
- int rc;
- pthread_getunique_np(&self, &tid);
- printf("Thread 0x%.8x %.8x entered\n", tid);
- errno = 0;
- rc = sleep(30);
- if (rc != 0 && errno == EINTR) {
- printf("Thread 0x%.8x %.8x got a signal delivered to it\n",
- tid);
- return NULL;
- }
- printf("Thread 0x%.8x %.8x did not get expected results! rc=%d, errno=%d\n",
- tid, rc, errno);
- return NULL;
- }
- int main(int argc, char **argv)
- {
- int rc;
- int i;
- struct sigaction actions;
- pthread_t threads[NUMTHREADS];
- printf("Enter Testcase - %s\n", argv[0]);
- printf("Set up the alarm handler for the process\n");
- memset(&actions, 0, sizeof(actions));
- sigemptyset(&actions.sa_mask);
- actions.sa_flags = 0;
- actions.sa_handler = sighand;
- rc = sigaction(SIGALRM,&actions,NULL);
- checkResults("sigaction\n", rc);
- for(i=0; i<NUMTHREADS; ++i) {
- rc = pthread_create(&threads[i], NULL, threadfunc, NULL);
- checkResults("pthread_create()\n", rc);
- }
- sleep(3);
- for(i=0; i<NUMTHREADS; ++i) {
- rc = pthread_kill(threads[i], SIGALRM);
- checkResults("pthread_kill()\n", rc);
- }
- for(i=0; i<NUMTHREADS; ++i) {
- rc = pthread_join(threads[i], NULL);
- checkResults("pthread_join()\n", rc);
- }
- printf("Main completed\n");
- return 0;
- }
- void sighand(int signo)
- {
- pthread_t self = pthread_self();
- pthread_id_np_t tid;
- pthread_getunique_np(&self, &tid);
- printf("Thread 0x%.8x %.8x in signal handler\n",
- tid);
- return;
- }
复制代码
运行输出为:
- Output:
- Enter Testcase - QP0WTEST/TPKILL0
- Set up the alarm handler for the process
- Thread 0x00000000 0000000c entered
- Thread 0x00000000 0000000d entered
- Thread 0x00000000 0000000e entered
- Thread 0x00000000 0000000c in signal handler
- Thread 0x00000000 0000000c got a signal delivered to it
- Thread 0x00000000 0000000d in signal handler
- Thread 0x00000000 0000000d got a signal delivered to it
- Thread 0x00000000 0000000e in signal handler
- Thread 0x00000000 0000000e got a signal delivered to it
- Main completed
复制代码
此法对于一般的操作也是非常可行的,可是在有的情况下就不是一个比较好的方法了,比如我们有一些线程在处理网络IO事件,假设它是一种一个客户端对应一个服务器线程,阻塞从Socket中读消息的情况。我们一般在网络IO的库里面回家上对EINTR信号的处理,例如recv时发现返回值小于0,检查error后,会进行他对应的操作。有可能他会再recv一次,那就相当于我的线程根本就不回终止,因为网络IO的类有可能不知道在获取EINTR时要终止线程。也就是说这不是一个特别好的可移植方案,如果你线程里的操作使用了很多外来的不太熟悉的类,而且你并不是他对EINTR的处理手段是什么,这是你在使用这样的方法来终止就有可能出问题了。而且如果你不是特别熟悉这方面的话你会很苦恼,“为什么我的测试代码全是ok的,一加入你们部门开发的框架进来就不ok了,肯定是你们框架出问题了”。好了,为了不必要的麻烦,我最后没有使用这个方案。
pthread_cancel
这个方案是我最终采用的方案,我认为是解决这个问题,通用的最好的解决方案,虽然前面其他方案的有些问题他可能也不好解决,但是相比较而言,还是相当不错的
pthread_cancel可以单独使用,因为在很多系统函数里面本身就有很多的断点,当调用这些系统函数时就会命中其内部的断点来结束线程,如下面的代码中, 即便注释掉我们自己设置的断点pthread_testcancel()程序还是一样的会被成功的cancel掉,因为printf函数内部有取消点 (如果大家想了解更多的函数的取消点情况,可以阅读《Unix高级环境编程》的线程部分)
- #include <pthread.h>
- #include <stdio.h>
- #include<stdlib.h>
- #include <unistd.h>
- void *threadfunc(void *parm)
- {
- printf("Entered secondary thread\n");
- while (1) {
- printf("Secondary thread is looping\n");
- pthread_testcancel();
- sleep(1);
- }
- return NULL;
- }
- int main(int argc, char **argv)
- {
- pthread_t thread;
- int rc=0;
- printf("Entering testcase\n");
- /* Create a thread using default attributes */
- printf("Create thread using the NULL attributes\n");
- rc = pthread_create(&thread, NULL, threadfunc, NULL);
- checkResults("pthread_create(NULL)\n", rc);
- /* sleep() is not a very robust way to wait for the thread */
- sleep(1);
- printf("Cancel the thread\n");
- rc = pthread_cancel(thread);
- checkResults("pthread_cancel()\n", rc);
- /* sleep() is not a very robust way to wait for the thread */
- sleep(10);
- printf("Main completed\n");
- return 0;
- }
复制代码
输出:
- Entering testcase
- Create thread using the NULL attributes
- Entered secondary thread
- Secondary thread is looping
- Cancel the thread
- Main completed
复制代码
POSIX保证了绝大部分的系统调用函数内部有取消点,我们看到很多在cancel调用的情景下,recv和send函数最后都会设置 pthread_testcancel() 取消点,其实这不是那么有必要的,那么究竟什么时候该 pthread_testcancel() 出场呢?《Unix高级环境编程》也说了,当遇到大量的基础计算时(如科学计算),需要自己来设置取消点。
ok,得益于pthread_cancel,我们很轻松的把线程可以cancel掉,可是我们的资源呢?何时释放...
下面来看两个pthread函数
1.void pthread_cleanup_push(void (*routine)(void *), void *arg);
2.void pthread_cleanup_pop(int execute);
这两个函数能够保证在 1函数调用之后,2函数调用之前的任何形式的线程结束调用向pthread_cleanup_push注册的回调函数
另外我们还可通过下面这个函数来设置一些状态
int pthread_setcanceltype(int type, int *oldtype);
当我们设置type为PTHREAD_CANCEL_ASYNCHRONOUS时,线程并不会等待命中取消点才结束,而是立马结束
好了,下面贴代码:
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <errno.h>
- int footprint=0;
- char *storage;
- void freerc(void *s)
- {
- free(s);
- puts("the free called");
- }
- static void checkResults(char *string, int rc) {
- if (rc) {
- printf("Error on : %s, rc=%d",
- string, rc);
- exit(EXIT_FAILURE);
- }
- return;
- }
- void *thread(void *arg) {
- int rc=0, oldState=0;
- rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); //close the cancel switch
- checkResults("pthread_setcancelstate()\n", rc);
- if ((storage = (char*) malloc(80)) == NULL) {
- perror("malloc() failed");
- exit(6);
- }
- rc = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&oldState); //open the cancel switch
- checkResults("pthread_setcancelstate(2)\n", rc);
- /* Plan to release storage even if thread doesn't exit normally */
- pthread_cleanup_push(freerc, storage); /*the free is method here you can use your own method here*/
- puts("thread has obtained storage and is waiting to be cancelled");
- footprint++;
- while (1)
- {
- pthread_testcancel(); //make a break point here
- //pthread_exit(NULL); //test exit to exam whether the freerc method called
- sleep(1);
- }
- pthread_cleanup_pop(1);
- }
- main() {
- pthread_t thid;
- void *status=NULL;
- if (pthread_create(&thid, NULL, thread, NULL) != 0) {
- perror("pthread_create() error");
- exit(1);
- }
- while (footprint == 0)
- sleep(1);
- puts("IPT is cancelling thread");
- if (pthread_cancel(thid) != 0) {
- perror("pthread_cancel() error");
- sleep(2);
- exit(3);
- }
- if (pthread_join(thid, &status) != 0) {
- if(status != PTHREAD_CANCELED){
- perror("pthread_join() error");
- exit(4);
- }
- }
- if(status == PTHREAD_CANCELED)
- puts("PTHREAD_CANCELED");
- puts("main exit");
- }
复制代码
pthread之如何正确的终止正在运行的子线程相关推荐
- 如何正确的终止正在运行的子线程
最近开发一些东西,线程数非常之多,当用户输入Ctrl+C的情形下,默认的信号处理会把程序退出,这时有可能会有很多线程的资源没有得到很好的释放,造成了内存泄露等等诸如此类的问题,本文就是围绕着这么一个使 ...
- c++thread里暂停线程_C语言如何正确的终止正在运行的子线程
最近开发一些东西,线程数非常之多,当用户输入Ctrl+C的情形下,默认的信号处理会把程序退出,这时有可能会有很多线程的资源没有得到很好的释放,造成了内存泄露等等诸如此类的问题,本文就是围绕着这么一个使 ...
- QTcpServer运行在子线程
QTcpServer运行在子线程 workserver.h workserver.cpp widget.h widget.cpp 运行结果 两种方法的比较 同步QThread的类 可重入性与线程安全 ...
- android如何终止一个正在运行的子线程
安卓的线程Run方法,执行一次就自动退出了,可以加入循环实现在Run方法中持续运行 线程像这样: Thread{boolean flag = false;run(){while(!flag){}} } ...
- python 事件通知模式_请问在 Python 的事件系统中,如何可以通过事件通知立刻终结一个正在运行的子线程?...
大家好,最近在完善手头上一个基于事件系统的 GUI . 现在遇到一个问题,就是当我在执行一个按钮点击事件的时候,实际会开一个子线程进行业务逻辑的处理,这个处理过程可能会比较长,并且中间可能会出现一些不 ...
- Python | threading01 - 创建两个同时运行的子线程
文章目录 一.前言 二.创建两个同时运行的线程 2.1.代码 2.2.运行 三.threading库的函数 3.1.threading.Thread() 3.2.threading.join() 3. ...
- MySQL 查看和终止正在运行的连接线程
文章目录 使用 SHOW 命令查看连接线程 使用 information_schema.processlist 使用 performance_schema.threads 使用 mysqladmin ...
- android广播怎样运行在子线程,android假如主线程依赖子线程A的执行结果,如何让A执行完成,之后主线程再往下执行呢?...
抛开你这段代码不看,单根据你的标题来回答: android假如主线程依赖子线程A的执行结果,如何让A执行完成,之后主线程再往下执行呢? 需要在子线程执行完成的地方,通过主线程的Handler发送一条消 ...
- Qt没有被正确安装,请运行make install问题的解决
本人是ubuntu 16.04系统,是在qtcreator中需要设置第三方厂家编译好的qt来进行编译,但始终报: Qt没有被正确安装,请运行make install 网上很多解决方法是基于window ...
最新文章
- Java--类的成员
- ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 ——材料清单
- jMeter的配置参数CookieManager.save.cookies
- python while无限循环、人为终止_Python while while循环永远不会停止,即使它应该
- javascript加密七种方法
- 解决mysql大小写敏感问题
- 上网行为管理系统服务器区域,上网行为管理服务器
- 详解BI系统中的任务调度
- BS7799、ISO/IEC 17799、ISO/IEC 27001 的关系
- Matlab中freqz函数使用
- 编写SQL语句,从Orders表中检索顾客ID(cust_id)和订单号(order_num),并先按顾客ID对结果进行排序,再按订单日期倒序排列
- 使用cubemx建立一个内部flash虚拟一个U盘的工程
- %2d, %02d, %d的区别
- java计算机毕业设计新疆旅游专列订票系统源码+mysql数据库+lw文档+系统+调试部署
- 升级到AndroidX,遇到appComponentFactory 错误
- 机器学习之Matplotlib
- 网站设计的思考 阿捷原创
- 高速数据采集卡——西安慕雷之产品
- IBM小型机+Oracle数据库+EMC存储设备,IOE简介
- 国产PAN3501三通道低功耗ASK接收125K底片唤醒芯片兼容替代AS3933