ae如何制作这种效果

下次自动登录
现在的位置:
& 综合 & 正文
如何正确的终止正在运行的子线程
  最近开发一些东西,线程数非常之多,当用户输入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))).表面上似乎所有的问题都能随着进程的结束来得到很好的处理,其实并不然,我们程序从堆上分配的内存就不能得到很好的释放,如new ,delete后的存储空间,这些空间进程结束并不会帮你把这部分内存归还给内存.(本文初稿时,因基础不牢固,此处写错,事实上无论进程这样结束,系统都将会释放掉所有所申请的资源,无论是堆上的还是栈上的。(感谢ZKey的指导)。这种结束所有线程(包括主线程)的方式实际上在很多时候是非常可取的,但是对于针对关闭时进行一些别的逻辑的处理(指非资源释放逻辑)就不会很好,例如我想在程序被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。当然我们同时也可以更改获取某个信号的行为,以此来达到我们终止子线程的目的。
1 #define _MULTI_THREADED 2 #include &pthread.h& 3 #include &stdio.h& 4 #include &signal.h& 5 #include "check.h" 6
7 #define NUMTHREADS 3 8 void sighand(int signo); 9 10 void *threadfunc(void *parm)11 {12
self = pthread_self();13
pthread_id_np_14
pthread_getunique_np(&self, &tid);17
printf("Thread 0x%.8x %.8x entered\n", tid);18
errno = 0;19
rc = sleep(30);20
if (rc != 0 && errno == EINTR) {21
printf("Thread 0x%.8x %.8x got a signal delivered to it\n",22
return NULL;24
printf("Thread 0x%.8x %.8x did not get expected results! rc=%d, errno=%d\n",26
tid, rc, errno);27
return NULL;28 }29 30 int main(int argc, char **argv)31 {32
struct siga35
threads[NUMTHREADS];36 37
printf("Enter Testcase - %s\n", argv[0]);38
printf("Set up the alarm handler for the process\n");40
memset(&actions, 0, sizeof(actions));41
sigemptyset(&actions.sa_mask);42
actions.sa_flags = 0;43
actions.sa_handler =44 45
rc = sigaction(SIGALRM,&actions,NULL);46
checkResults("sigaction\n", rc);47 48
for(i=0; i&NUMTHREADS; ++i) {49
rc = pthread_create(&threads[i], NULL, threadfunc, NULL);50
checkResults("pthread_create()\n", rc);51
sleep(3);54
for(i=0; i&NUMTHREADS; ++i) {55
rc = pthread_kill(threads[i], SIGALRM);56
checkResults("pthread_kill()\n", rc);57
for(i=0; i&NUMTHREADS; ++i) {60
rc = pthread_join(threads[i], NULL);61
checkResults("pthread_join()\n", rc);62
printf("Main completed\n");64
return 0;65 }66 67 void sighand(int signo)68 {69
self = pthread_self();70
pthread_id_np_71
pthread_getunique_np(&self, &tid);73
printf("Thread 0x%.8x %.8x in signal handler\n",74
return;76 }
运行输出为:
1 Output: 2
3 Enter Testcase - QP0WTEST/TPKILL0 4 Set up the alarm handler for the process 5 Thread 0x 0000000c entered 6 Thread 0x 0000000d entered 7 Thread 0x 0000000e entered 8 Thread 0x 0000000c in signal handler 9 Thread 0x 0000000c got a signal delivered to it10 Thread 0x 0000000d in signal handler11 Thread 0x 0000000d got a signal delivered to it12 Thread 0x 0000000e in signal handler13 Thread 0x 0000000e got a signal delivered to it14 Main completed
  我们可以通过截获的signal信号,来释放掉线程申请的资源,可是遗憾的是我们不能再signal处理里调用pthread_exit来终结掉线程,因为pthread_exit是中介当前线程,而signal被调用的方式可以理解为内核的回调,不是在同一个线程运行的,所以这里只能做处理释放资源的事情,线程内部只有判断有没有被中断(一般是EINTR)来断定是否要求自己结束,判定后可以调用pthread_exit退出。
  此法对于一般的操作也是非常可行的,可是在有的情况下就不是一个比较好的方法了,比如我们有一些线程在处理网络IO事件,假设它是一种一个客户端对应一个服务器线程,阻塞从Socket中读消息的情况。我们一般在网络IO的库里面回家上对EINTR信号的处理,例如recv时发现返回值小于0,检查error后,会进行他对应的操作。有可能他会再recv一次,那就相当于我的线程根本就不回终止,因为网络IO的类有可能不知道在获取EINTR时要终止线程。也就是说这不是一个特别好的可移植方案,如果你线程里的操作使用了很多外来的不太熟悉的类,而且你并不是他对EINTR的处理手段是什么,这是你在使用这样的方法来终止就有可能出问题了。而且如果你不是特别熟悉这方面的话你会很苦恼,“为什么我的测试代码全是ok的,一加入你们部门开发的框架进来就不ok了,肯定是你们框架出问题了”。好了,为了不必要的麻烦,我最后没有使用这个方案。
pthread_cancel
  这个方案是我最终采用的方案,我认为是解决这个问题,通用的最好的解决方案,虽然前面其他方案的有些问题他可能也不好解决,但是相比较而言,还是相当不错的
pthread_cancel可以单独使用,因为在很多系统函数里面本身就有很多的断点,当调用这些系统函数时就会命中其内部的断点来结束线程,如下面的代码中,即便注释掉我们自己设置的断点pthread_testcancel()程序还是一样的会被成功的cancel掉,因为printf函数内部有取消点(如果大家想了解更多的函数的取消点情况,可以阅读《Unix高级环境编程》的线程部分)
1 #include &pthread.h& 2 #include &stdio.h& 3 #include&stdlib.h& 4 #include &unistd.h& 5 void *threadfunc(void *parm) 6 { 7
printf("Entered secondary thread\n"); 8
while (1) { 9
printf("Secondary thread is looping\n");10
pthread_testcancel();11
sleep(1);12
return NULL;14 }15 16 int main(int argc, char **argv)17 {18
pthread_19
rc=0;20 21
printf("Entering testcase\n");22 23
/* Create a thread using default attributes */24
printf("Create thread using the NULL attributes\n");25
rc = pthread_create(&thread, NULL, threadfunc, NULL);26
checkResults("pthread_create(NULL)\n", rc);27 28
/* sleep() is not a very robust way to wait for the thread */29
sleep(1);30 31
printf("Cancel the thread\n");32
rc = pthread_cancel(thread);33
checkResults("pthread_cancel()\n", rc);34 35
/* sleep() is not a very robust way to wait for the thread */36
sleep(10);37
printf("Main completed\n");38
return 0;39 }
Entering testcaseCreate thread using the NULL attributesEntered secondary threadSecondary thread is loopingCancel the threadMain 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);
Cancelability
Cancelability State
Cancelability Type
PTHREAD_CANCEL_DISABLE
PTHREAD_CANCEL_DEFERRED
PTHREAD_CANCEL_DISABLE
PTHREAD_CANCEL_ASYNCHRONOUS
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DEFERRED
asynchronous
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_ASYNCHRONOUS
  当我们设置type为PTHREAD_CANCEL_ASYNCHRONOUS时,线程并不会等待命中取消点才结束,而是立马结束
  好了,下面贴代码:
1 #include &pthread.h&
2 #include &stdio.h&
3 #include &stdlib.h&
4 #include &unistd.h&
5 #include &errno.h&
6 int footprint=0; 7 char *
8 void freerc(void *s) 9 {10
free(s);11
puts("the free called");
13 14 static void checkResults(char *string, int rc) {15
if (rc) {16
printf("Error on : %s, rc=%d",17
string, rc);18
exit(EXIT_FAILURE);19
return;21 }22
23 void *thread(void *arg) {
rc=0, oldState=0; 25
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");
rc = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&oldState);
//open the cancel
checkResults("pthread_setcancelstate(2)\n", rc);
33 /* 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)40
pthread_testcancel();
//make a break point here
//pthread_exit(NULL);
//test exit to exam whether the freerc method called
pthread_cleanup_pop(1);
49 main() {
pthread_ 51
*status=NULL;
if (pthread_create(&thid, NULL, thread, NULL) != 0) {
perror("pthread_create() error");
while (footprint == 0)
puts("IPT is cancelling thread");
if (pthread_cancel(thid) != 0) {
perror("pthread_cancel() error");
if (pthread_join(thid, &status) != 0) {
if(status != PTHREAD_CANCELED){
perror("pthread_join() error");
exit(4);73
if(status == PTHREAD_CANCELED)76
puts("PTHREAD_CANCELED");77
puts("main exit");
&&&&推荐文章:
【上篇】【下篇】java中,如何安全的结束一个正在运行的线程?
java中,如何安全的结束一个正在运行的线程?
  问题  Java中提供了很多调度线程的方法,上一节介绍了其中一种控制线程的方法:如何等待一个线程结束。那么如果不希望等待线程结束,而是根据问题的需要随时都要中断线程使其结束,这种对线程的控制方法该如何实现呢?  解决思路  首先必须先明确“中断”这个概念的实际含义,这里的中断是指一个线程在其任务完成之前被强行停止,提前消亡的过程。查阅JDK的帮助文档,可以找到这样一个和中断有关的方法:interrupt()。  它的语法格式如下所示:  public void interrupt()  该方法的功能是中断一个线程的执行。但是,在实际使用当中发现,这个方法不一定能够真地中断一个正在运行的线程。下面通过一个例子来看一看使用interrput()方法中断一个线程时所出现的结果。程序代码如下所示:  // 例4.4.1 InterruptThreadDemo.java  class MyThread extends Thread  {  public void run()  {  while(true) // 无限循环,并使线程每隔1秒输出一次字符串  {&  System.out.println(getName()+' is running');&  try{&  sleep(1000);  }catch(InterruptedException e){  System.out.println(e.getMessage());  }  }  }  }  class InterruptThreadDemo  {  public static void main(String[] args) throws InterruptedException  {  MyThread m=new MyThread(); // 创建线程对象m  System.out.println('Starting thread...');  m.start(); // 启动线程m  Thread.sleep(2000); //主线程休眠2秒,使线程m一直得到执行  System.out.println('Interrupt thread...');  m.interrupt(); // 调用interrupt()方法中断线程m  Thread.sleep(2000); // 主线程休眠2秒,观察中断后的结果  System.out.println('Stopping application...'); // 主线程结束  }  }  这个程序的本意是希望,当程序执行到m.interrupt()方法后,线程m将被中断并进入消亡状态。然而运行这个程序,屏幕里显示了出人意料的结果,如图4.4.1所示。  通过对结果的分析,可以发现,用户线程在调用了interrupt()方法之后并没有被中断,而是继续执行,直到人为地按下Ctrl+C或者Pause键为止。这个例子说明一个事实,直接使用interrput()方法并不能中断一个正在运行的线程。那么用什么样的方法才能中断一个正在运行的线程呢?    图 4.4.1 对线程调用了interrupt()  通过查阅JDK,有些读者可能会看到Thread类中所提供的stop()方法。但是在这里需要强调的是,虽然该方法确实能够停止一个正在运行的线程,但是该方法是不安全的,因为有时使用它会导致严重的系统错误。例如一个线程正在等待关键的数据结构,并只完成了部分地改变,如果在这一时刻停止该线程,那么数据结构将会停留在错误的状态上。正因为如此,在Java后期的版本中,它将不复存在。因此,使用stop()方法来中断一个线程是不合适的。  这时我们想到了使用共享变量的方式,通过一个共享信号变量来通知线程是否需要中断,如果需要中断,则停止正在执行的任务,否则让任务继续执行。这种方式是如何实现的呢?  具体步骤  在这种方式中,之所以引入共享变量,是因为该变量可以被多个执行相同任务的线程用来作为是否中断的信号,通知中断线程的执行。下面通过在程序中引入共享变量来改进前面例4.4.1,改进后的代码如下所示:  // 例4.4.2 InterruptThreadDemo2.java  class MyThread extends Thread  {  boolean stop = // 引入一个布尔型的共享变量stop  public void run()  {  while(!stop) // 通过判断stop变量的值来确定是否继续执行线程体  {  System.out.println(getName()+' is running');  try  {  sleep(1000);&  }catch(InterruptedException e){  System.out.println(e.getMessage());  }  }  System.out.println('Thread is exiting...');  }  }  class InterruptThreadDemo2  {  public static void main(String[] args) throws InterruptedException  {  MyThread m=new MyThread();  System.out.println('Starting thread...');  m.start();  Thread.sleep(3000);&  System.out.println('Interrupt thread...');  m.stop= // 修改共享变量  Thread.sleep(3000); // 主线程休眠以观察线程中断后的情况  System.out.println('Stopping application...');  }  }  在使用共享变量来中断一个线程的过程中,线程体通过循环来周期性的检查这一变量的状态。如果变量的状态改变,说明程序发出了立即中断该线程的请求,此时,循环体条件不再满足,结束循环,进而结束线程的任务。程序执行的结果如图4.4.2所示:    图4.4.2 引入共享变量来中断线程  其中,主程序中的第二个Thread.sleep(3000);语句就是用来使程序不提早结束,以便观察线程m的中断情况。结果是一旦将共享变量stop设置为true,则中断立即发生。  为了更加安全起见,通常需要将共享变量定义为volatile类型或者将对该共享变量的一切访问封装到同步的代码或者同步方法中去。后者所提到的技术将在第4.5节中介绍。  在多线程的程序中,当出现有两个或多个线程共享同一实例变量的情况时,每一个线程可以保持这个实例变量自己的私有副本,变量的实际备份在不同时间被更新。而问题就是变量的主备份总是需要反映它的当前状态,此时反而使效率降低。为保证效率,只需要简单地指定变量为volatile类型即可,它可以告诉编译器必须总是使用volatile变量的主备份(或者至少总是保持任何私有的备份和最新的备份一样,反之亦然)。同样,对主变量的访问必须同任何私有备份一样,精确地顺序执行。  如果需要一次中断所有由同一线程类创建的线程,该怎样实现呢?有些读者可能马上就想到了对每一个线程对象通过设置共享变量的方式来中断线程。这种方法当然可以,那么有没有更好的方法呢?  此时只需将共享变量设置为static类型的即可。然后在主程序中当需要中断所有同一个线程类创建的线程对象时,使用MyThread.stop=语句就可实现对所有同一个线程类创建的线程对象的中断操作,而且效率明显提高。读者不妨试一试。  专家说明  通过本节介绍了如何中断一个正在执行的线程,既不是用stop()方法,也不是用interrupt()方法,而是通过引入了共享变量的形式有效地解决了线程中断的问题。其实这种方法有很多好处,它避免了一些无法想象的意外情况的发生,特别是将共享变量所访问的一切代码都封装到同步方法中以后,安全性将更高。在本节中,还可以尝试创建多个线程来检验这种中断方式的好处。此外,还介绍了volatile类型说明符的作用,这更加有助于提高中断线程的效率,值得提倡。  专家指点  本小节不仅要掌握如何使用共享变量的方法来中断一个线程,还要明白为什么使用其他方法来中断线程就不安全。其实,在多线程的调度当中还会出现一个问题,那就是死锁。死锁的出现将导致线程间均无法向前推进,从而陷入尴尬的局面。因此,为减少出现死锁的发生,Java 1.2以后的版本中已经不再使用Thread类的stop(),suspend(),resume()以及destroy()方法。特别是不安全的stop()方法,原因就是它会解除由线程获取的所有锁定,而且一旦对象处于一种不连贯的状态,那么其他线程就能在那种状态下检查和修改它们,结果导致很难再检查出问题的真正所在。因此最好的方法就是,用一个标志来告诉线程什么时候应该退出自己的run()方法,并中断自己的执行。通过后面小节的学习将会更好的理解这个问题。  相关问题  如果一个线程由于等待某些事件的发生而被阻塞,又该如何实现该线程的中断呢?比如当一个线程由于需要等候键盘输入而被阻塞,处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。  其实,这种情况经常会发生,比如调用Thread.join()方法,或者Thread.sleep()方法,在网络中调用ServerSocket.accept()方法,或者调用了DatagramSocket.receive()方法时,都有可能导致线程阻塞。即便这样,仍然不要使用stop()方法,而是使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码。  下面看一个例子来说明这个问题:  // 例4.4.3 InterruptThreadDemo3.java  class MyThread extends Thread  {  volatile boolean stop =  public void run()  {  while(!stop)  {  System.out.println(getName()+' is running');  try  {  sleep(1000);  }catch(InterruptedException e){&  System.out.println('week up from blcok...');  stop= // 在异常处理代码中修改共享变量的状态  }  }  System.out.println(getName()+' is exiting...');  }  }  class InterruptThreadDemo3  {  public static void main(String[] args) throws InterruptedException  {  MyThread m1=new MyThread();  System.out.println('Starting thread...');  m1.start();  Thread.sleep(3000);&  System.out.println('Interrupt thread...:'+m1.getName());  m1.stop= // 设置共享变量为true  m1.interrupt(); // 阻塞时退出阻塞状态  Thread.sleep(3000); // 主线程休眠3秒以便观察线程m1的中断情况  System.out.println('Stopping application...');  }  }  程序中如果线程m1发生了阻塞,那么虽然执行了m1.stop=语句,但是stop的值并未改变。为了能够中断该线程,必须在异常处理语句中对共享变量的值进行重新设置,从而实现了在任何情况下都能够中断线程的目的。  一定要记住,m1.interrupt();语句只有当线程发生阻塞时才有效。它的作用就是抛出一个InterruptedException类的异常对象,使try…catch语句捕获异常,并对其进行处理。请读者仔细研究这个程序,以便能够看出其中的巧妙之处。
发表评论:
TA的最新馆藏[转]&  最近开发一些东西,线程数非常之多,当用户输入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))).表面上似乎所有的问题都能随着进程的结束来得到很好的处理,其实并不然,我们程序从堆上分配的内存就不能得到很好的释放,如new ,delete后的存储空间,这些空间进程结束并不会帮你把这部分内存归还给内存.(本文初稿时,因基础不牢固,此处写错,事实上无论进程这样结束,系统都将会释放掉所有代码所申请的资源,无论是堆上的还是栈上的。(感谢ZKey的指导)。这种结束所有线程(包括主线程)的方式实际上在很多时候是非常可取的,但是对于针对关闭时进行一些别的逻辑的处理(指非资源释放逻辑)就不会很好,例如我想在程序被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。当然我们同时也可以更改获取某个信号的行为,以此来达到我们终止子线程的目的。
&1&#define&_MULTI_THREADED
&2&#include&&pthread.h&
&3&#include&&stdio.h&
&4&#include&&signal.h&
&5&#include&"check.h"
&7&#define&NUMTHREADS&3
&8&void&sighand(int&signo);
10&void&*threadfunc(void&*parm)
12&&&pthread_t&&&&&&&&&&&&&self&=&pthread_self();
13&&&pthread_id_np_t&&&&&&&
14&&&int&&&&&&&&&&&&&&&&&&&
16&&&pthread_getunique_np(&self,&&tid);
17&&&printf("Thread&0x%.8x&%.8x&entered\n",&tid);
18&&&errno&=&0;
19&&&rc&=&sleep(30);
20&&&if&(rc&!=&0&&&&errno&==&EINTR)&{
21&&&&&printf("Thread&0x%.8x&%.8x&got&a&signal&delivered&to&it\n",
22&&&&&&&&&&&&tid);
23&&&&&return&NULL;
25&&&printf("Thread&0x%.8x&%.8x&did&not&get&expected&results!&rc=%d,&errno=%d\n",
26&&&&&&&&&&tid,&rc,&errno);
27&&&return&NULL;
30&int&main(int&argc,&char&**argv)
32&&&int&&&&&&&&&&&&&&&&&&&&&
33&&&int&&&&&&&&&&&&&&&&&&&&&i;
34&&&struct&sigaction&&&&&&&&
35&&&pthread_t&&&&&&&&&&&&&&&threads[NUMTHREADS];
37&&&printf("Enter&Testcase&-&%s\n",&argv[0]);
39&&&printf("Set&up&the&alarm&handler&for&the&process\n");
40&&&memset(&actions,&0,&sizeof(actions));
41&&&sigemptyset(&actions.sa_mask);
42&&&actions.sa_flags&=&0;
43&&&actions.sa_handler&=&
45&&&rc&=&sigaction(SIGALRM,&actions,NULL);
46&&&checkResults("sigaction\n",&rc);
48&&&for(i=0;&i&NUMTHREADS;&++i)&{
49&&&&&rc&=&pthread_create(&threads[i],&NULL,&threadfunc,&NULL);
50&&&&&checkResults("pthread_create()\n",&rc);
53&&&sleep(3);
54&&&for(i=0;&i&NUMTHREADS;&++i)&{
55&&&&&rc&=&pthread_kill(threads[i],&SIGALRM);
56&&&&&checkResults("pthread_kill()\n",&rc);
59&&&for(i=0;&i&NUMTHREADS;&++i)&{
60&&&&&rc&=&pthread_join(threads[i],&NULL);
61&&&&&checkResults("pthread_join()\n",&rc);
63&&&printf("Main&completed\n");
64&&&return&0;
67&void&sighand(int&signo)
69&&&pthread_t&&&&&&&&&&&&&self&=&pthread_self();
70&&&pthread_id_np_t&&&&&&&
72&&&pthread_getunique_np(&self,&&tid);
73&&&printf("Thread&0x%.8x&%.8x&in&signal&handler\n",
74&&&&&&&&&&tid);
75&&&return;
&运行输出为:
&1&Output:
&3&Enter&Testcase&-&QP0WTEST/TPKILL0
&4&Set&up&the&alarm&handler&for&the&process
&5&Thread&0x&0000000c&entered
&6&Thread&0x&0000000d&entered
&7&Thread&0x&0000000e&entered
&8&Thread&0x&0000000c&in&signal&handler
&9&Thread&0x&0000000c&got&a&signal&delivered&to&it
10&Thread&0x&0000000d&in&signal&handler
11&Thread&0x&0000000d&got&a&signal&delivered&to&it
12&Thread&0x&0000000e&in&signal&handler
13&Thread&0x&0000000e&got&a&signal&delivered&to&it
14&Main&completed
  我们可以通过截获的signal信号,来释放掉线程申请的资源,可是遗憾的是我们不能再signal处理里调用pthread_exit来终结掉线程,因为pthread_exit是中介当前线程,而signal被调用的方式可以理解为内核的回调,不是在同一个线程运行的,所以这里只能做处理释放资源的事情,线程内部只有判断有没有被中断(一般是EINTR)来断定是否要求自己结束,判定后可以调用pthread_exit退出。
&  此法对于一般的操作也是非常可行的,可是在有的情况下就不是一个比较好的方法了,比如我们有一些线程在处理网络IO事件,假设它是一种一个客户端对应一个服务器线程,阻塞从Socket中读消息的情况。我们一般在网络IO的库里面回家上对EINTR信号的处理,例如recv时发现返回值小于0,检查error后,会进行他对应的操作。有可能他会再recv一次,那就相当于我的线程根本就不回终止,因为网络IO的类有可能不知道在获取EINTR时要终止线程。也就是说这不是一个特别好的可移植方案,如果你线程里的操作使用了很多外来的不太熟悉的类,而且你并不是他对EINTR的处理手段是什么,这是你在使用这样的方法来终止就有可能出问题了。而且如果你不是特别熟悉这方面的话你会很苦恼,&为什么我的测试代码全是ok的,一加入你们部门开发的框架进来就不ok了,肯定是你们框架出问题了&。好了,为了不必要的麻烦,我最后没有使用这个方案。
pthread_cancel
  这个方案是我最终采用的方案,我认为是解决这个问题,通用的最好的解决方案,虽然前面其他方案的有些问题他可能也不好解决,但是相比较而言,还是相当不错的
pthread_cancel可以单独使用,因为在很多系统函数里面本身就有很多的断点,当调用这些系统函数时就会命中其内部的断点来结束线程,如下面的代码中,即便注释掉我们自己设置的断点pthread_testcancel()程序还是一样的会被成功的cancel掉,因为printf函数内部有取消点(如果大家想了解更多的函数的取消点情况,可以阅读《Unix高级环境编程》的线程部分)
&1&#include&&pthread.h&
&2&#include&&stdio.h&
&3&#include&stdlib.h&
&4&#include&&unistd.h&
&5&void&*threadfunc(void&*parm)
&7&&&printf("Entered&secondary&thread\n");
&8&&&while&(1)&{
&9&&&&&printf("Secondary&thread&is&looping\n");
10&&&&&pthread_testcancel();
11&&&&&sleep(1);
13&&&return&NULL;
16&int&main(int&argc,&char&**argv)
18&&&pthread_t&&&&&&&&&&&&&
19&&&int&&&&&&&&&&&&&&&&&&&rc=0;
21&&&printf("Entering&testcase\n");
23&&&/*&Create&a&thread&using&default&attributes&*/
24&&&printf("Create&thread&using&the&NULL&attributes\n");
25&&&rc&=&pthread_create(&thread,&NULL,&threadfunc,&NULL);
26&&&checkResults("pthread_create(NULL)\n",&rc);
28&&&/*&sleep()&is&not&a&very&robust&way&to&wait&for&the&thread&*/
29&&&sleep(1);
31&&&printf("Cancel&the&thread\n");
32&&&rc&=&pthread_cancel(thread);
33&&&checkResults("pthread_cancel()\n",&rc);
35&&&/*&sleep()&is&not&a&very&robust&way&to&wait&for&the&thread&*/
36&&&sleep(10);
37&&&printf("Main&completed\n");
38&&&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);&
CancelabilityCancelability StateCancelability Type
PTHREAD_CANCEL_DISABLE
PTHREAD_CANCEL_DEFERRED
PTHREAD_CANCEL_DISABLE
PTHREAD_CANCEL_ASYNCHRONOUS
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DEFERRED
asynchronous
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_ASYNCHRONOUS
  当我们设置type为PTHREAD_CANCEL_ASYNCHRONOUS时,线程并不会等待命中取消点才结束,而是立马结束
  好了,下面贴代码:
&1&#include&&pthread.h&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&2&#include&&stdio.h&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&3&#include&&stdlib.h&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&4&#include&&unistd.h&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&5&#include&&errno.h&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&6&int&footprint=0;
&7&char&*&
&8&void&freerc(void&*s)
10&&&&free(s);
11&&&&puts("the&free&called");&&&&
12&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
14&static&void&checkResults(char&*string,&int&rc)&{
15&&&if&(rc)&{
16&&&&&printf("Error&on&:&%s,&rc=%d",
17&&&&&&&&&&&&string,&rc);
18&&&&&exit(EXIT_FAILURE);
20&&&return;
22&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
23&void&*thread(void&*arg)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
24&&&int&&&&&&&&&&&rc=0,&oldState=0;&
25&&&rc&=&pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&&oldState);&&&//close&the&cancel&switch&&&
26&&&checkResults("pthread_setcancelstate()\n",&rc);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
27&&&if&((storage&=&(char*)&malloc(80))&==&NULL)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
28&&&&&perror("malloc()&failed");&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
29&&&&&exit(6);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
30&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
31&&&&rc&=&pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&oldState);&&&//open&the&cancel&&switch&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
32&&&&checkResults("pthread_setcancelstate(2)\n",&rc);&&
33&/*&Plan&to&release&storage&even&if&thread&doesn't&exit&normally&*/&&&&&&&&&&&&
34&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
35&&&pthread_cleanup_push(freerc,&storage);&&&/*the&free&is&method&here&&&you&can&use&your&own&method&here*/&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
36&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
37&&&puts("thread&has&obtained&storage&and&is&waiting&to&be&cancelled");&&&&&&&&&&&
38&&&footprint++;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
39&&&while&(1)
40&&&&&{&&&&&&
41&&&&&&&&&&pthread_testcancel();&&&//make&a&break&point&here&&&&&
42&&&&&&&&&&&&//pthread_exit(NULL);&&&&//test&exit&to&exam&whether&the&freerc&method&called&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
43&&&&&&&&&&&&&sleep(1);&&
44&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
45&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
46&&&pthread_cleanup_pop(1);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
47&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
48&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
49&main()&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
50&&&pthread_t&&
51&&void&&&&&&&&&&&&&&&&&*status=NULL;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
52&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
53&&&if&(pthread_create(&thid,&NULL,&thread,&NULL)&!=&0)&{&&&&&&&&&&&&&&&&&&&&&&&&&
54&&&&&perror("pthread_create()&error");&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
55&&&&&exit(1);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
56&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
57&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
58&&&while&(footprint&==&0)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
59&&&&&sleep(1);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
60&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
61&&&puts("IPT&is&cancelling&thread");&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
62&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
63&&&if&(pthread_cancel(thid)&!=&0)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
64&&&&&perror("pthread_cancel()&error");&&&
65&&&&&sleep(2);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
66&&&&&exit(3);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
67&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
68&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
69&&&if&(pthread_join(thid,&&status)&!=&0)&{&&
70&&&&&if(status&!=&PTHREAD_CANCELED){&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
71&&&&&&&&&perror("pthread_join()&error");&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
72&&&&&&&&&exit(4);
73&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
75&&if(status&==&PTHREAD_CANCELED)
76&&&&&puts("PTHREAD_CANCELED");
78&&puts("main&exit");&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
阅读(...) 评论()

我要回帖

 

随机推荐