亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

C語言如何正確的終止正在運行的子線程

2020-01-26 13:32:13
字體:
來源:轉載
供稿:網友

最近開發一些東西,線程數非常之多,當用戶輸入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的指導)。這種結束所有線程(包括主線程)的方式實際上在很多時候是非??扇〉模菍τ卺槍﹃P閉時進行一些別的邏輯的處理(指非資源釋放邏輯)就不會很好,例如我想在程序被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 3void 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

我們可以通過截獲的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高級環境編程》的線程部分)

#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);

Cancelability Cancelability State Cancelability Type
disabled PTHREAD_CANCEL_DISABLE PTHREAD_CANCEL_DEFERRED
disabled PTHREAD_CANCEL_DISABLE PTHREAD_CANCEL_ASYNCHRONOUS
deferred PTHREAD_CANCEL_ENABLE PTHREAD_CANCEL_DEFERRED
asynchronous PTHREAD_CANCEL_ENABLE PTHREAD_CANCEL_ASYNCHRONOUS

當我們設置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");                                    }

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩亚洲精品一区二区三区| 国产偷国产偷亚洲清高网站| www.欧美精品一二三区| 久久伊人精品天天| 精品亚洲一区二区三区在线观看| 日韩电影免费在线观看| 人人做人人澡人人爽欧美| 亚洲精品欧美极品| 欧美午夜精品久久久久久人妖| 亚洲一区二区三区在线视频| 色婷婷av一区二区三区久久| 国产视频亚洲视频| 91视频国产精品| 亚洲free性xxxx护士白浆| 国内免费精品永久在线视频| 欧美在线免费视频| 一本色道久久综合狠狠躁篇的优点| 丁香五六月婷婷久久激情| 亚洲最大av在线| 欧美日韩另类字幕中文| 91精品视频一区| 亚洲欧美激情四射在线日| 国产激情综合五月久久| 久久69精品久久久久久国产越南| 国产精品网站入口| 国产亚洲欧美日韩美女| 久久久久久久久电影| 国内精品久久久久久久久| 97视频在线观看免费高清完整版在线观看| 91精品在线一区| 久久夜色精品国产欧美乱| 日韩hd视频在线观看| 国模视频一区二区三区| 欧美人在线观看| 国产精品日日摸夜夜添夜夜av| 影音先锋日韩有码| 最近2019年手机中文字幕| 成人性教育视频在线观看| 精品一区二区三区三区| 亚洲精品狠狠操| 国产精品久久久久久久久男| 国产精品va在线播放我和闺蜜| 国产国语videosex另类| 久久久在线视频| 亚洲肉体裸体xxxx137| 欧美最猛性xxxxx(亚洲精品)| 欧美人成在线视频| 亚洲免费视频在线观看| 欧美猛少妇色xxxxx| 奇米成人av国产一区二区三区| 中文字幕亚洲一区二区三区| 欧美性受xxxx黑人猛交| 在线日韩中文字幕| 91精品国产综合久久香蕉最新版| 久久影视电视剧凤归四时歌| 欧美日韩国产综合视频在线观看中文| 中文亚洲视频在线| 尤物精品国产第一福利三区| 成人av番号网| 欧美性猛交xxxx黑人猛交| 成人免费在线视频网站| 亚洲无亚洲人成网站77777| 538国产精品一区二区免费视频| 欧美性高潮床叫视频| 国产精品91久久久| 成人黄色免费片| 欧美成人免费全部观看天天性色| 精品亚洲一区二区三区在线播放| 欧美高清不卡在线| 奇米成人av国产一区二区三区| 午夜精品久久久久久99热| 久久这里有精品| www.亚洲成人| 国产成人高潮免费观看精品| 亚洲欧美视频在线| 国产69久久精品成人| 中国人与牲禽动交精品| 亚洲二区在线播放视频| 欧美日韩xxx| 国产一区二区免费| 久久亚洲春色中文字幕| 国产精品美乳一区二区免费| 亚洲免费视频观看| 日韩人在线观看| 日韩天堂在线视频| 在线日韩欧美视频| 国产精品久久久久999| 国产精品久久久久秋霞鲁丝| 国产精品一区二区女厕厕| 国产一区玩具在线观看| 国产成人精品视频| 668精品在线视频| 日韩视频免费大全中文字幕| 亚洲自拍高清视频网站| 亚洲精品v欧美精品v日韩精品| 91青草视频久久| 亚洲护士老师的毛茸茸最新章节| 国产日韩综合一区二区性色av| 欧美影院在线播放| 欧美极品欧美精品欧美视频| 在线日韩av观看| 亚洲福利在线视频| 亚洲精品97久久| 国产成人福利网站| 久久99热精品这里久久精品| 国产精品丝袜一区二区三区| 国产91在线高潮白浆在线观看| 日本高清久久天堂| 欧美成人剧情片在线观看| 成人精品一区二区三区电影黑人| 91中文在线观看| 久久久国产一区二区三区| 亚洲国产精久久久久久久| 操91在线视频| 992tv成人免费视频| 国产一区二区三区视频免费| 精品日韩中文字幕| 亚洲第一页中文字幕| 欧美激情欧美狂野欧美精品| 亚洲天堂色网站| 日韩欧美国产黄色| 亚洲欧洲一区二区三区在线观看| 国产精品福利网站| 日韩欧美亚洲范冰冰与中字| 美女撒尿一区二区三区| 国产精品入口夜色视频大尺度| 亚洲成人精品视频在线观看| 久久av在线播放| 久青草国产97香蕉在线视频| 日韩精品在线观看一区二区| 亚洲一区二区免费在线| 亚洲精品综合久久中文字幕| 这里只有精品视频在线| 中文字幕国产日韩| 免费97视频在线精品国自产拍| 中文字幕精品网| 一区二区三区久久精品| 一区二区福利视频| 亚洲天堂网在线观看| 日韩av不卡在线| 欧美一级高清免费播放| 久久青草精品视频免费观看| 日韩亚洲综合在线| 日本成人免费在线| 成人在线中文字幕| 色悠久久久久综合先锋影音下载| 91精品国产沙发| 日韩欧美国产中文字幕| 日韩免费在线播放| 国产精品视频精品| 91免费视频网站| 国产精品白丝av嫩草影院| 久久久人成影片一区二区三区| 欧美激情欧美激情在线五月| 欧美日韩国产在线播放| 成人精品在线视频| 国产精品自拍小视频| 久久青草精品视频免费观看| 国产精品久久久久久久7电影| 成人中文字幕在线观看| 91精品久久久久久久久青青| 456亚洲影院| 欧美性xxxxxxxxx| 日韩精品中文字幕视频在线|