Posix線程編程指南(3)

      網友投稿 836 2022-05-29

      一.互斥鎖

      盡管在Posix Thread中同樣可以使用IPC的信號量機制來實現互斥鎖mutex功能,但顯然semphore的功能過于強大了,在Posix Thread中定義了另外一套專門用于線程同步的mutex函數。

      1. 創建和銷毀

      有兩種方法創建互斥鎖,靜態方式和動態方式。POSIX定義了一個宏PTHREAD_MUTEX_INITIALIZER來靜態初始化互斥鎖,方法如下:

      pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

      在LinuxThreads實現中,pthread_mutex_t是一個結構,而PTHREAD_MUTEX_INITIALIZER則是一個結構常量。

      動態方式是采用pthread_mutex_init()函數來初始化互斥鎖,API定義如下:

      int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t ? *mutexattr)

      其中mutexattr用于指定互斥鎖屬性(見下),如果為NULL則使用缺省屬性。

      pthread_mutex_destroy()用于注銷一個互斥鎖,API定義如下:

      int pthread_mutex_destroy(pthread_mutex_t *mutex)

      Posix線程編程指南(3)

      銷毀一個互斥鎖即意味著釋放它所占用的資源,且要求鎖當前處于開放狀態。由于在Linux中,互斥鎖并不占用任何資源,因此LinuxThreads中的pthread_mutex_destroy()除了檢查鎖狀態以外(鎖定狀態則返回EBUSY)沒有其他動作。

      2. 互斥鎖屬性

      互斥鎖的屬性在創建鎖的時候指定,在LinuxThreads實現中僅有一個鎖類型屬性,不同的鎖類型在試圖對一個已經被鎖定的互斥鎖加鎖時表現不同。當前(glibc2.2.3,linuxthreads0.9)有四個值可供選擇:

      PTHREAD_MUTEX_TIMED_NP,這是缺省值,也就是普通鎖。當一個線程加鎖以后,其余請求鎖的線程將形成一個等待隊列,并在解鎖后按優先級獲得鎖。這種鎖策略保證了資源分配的公平性。

      PTHREAD_MUTEX_RECURSIVE_NP,嵌套鎖,允許同一個線程對同一個鎖成功獲得多次,并通過多次unlock解鎖。如果是不同線程請求,則在加鎖線程解鎖時重新競爭。

      PTHREAD_MUTEX_ERRORCHECK_NP,檢錯鎖,如果同一個線程請求同一個鎖,則返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP類型動作相同。這樣就保證當不允許多次加鎖時不會出現最簡單情況下的死鎖。

      PTHREAD_MUTEX_ADAPTIVE_NP,適應鎖,動作最簡單的鎖類型,僅等待解鎖后重新競爭。

      3. 鎖操作

      鎖操作主要包括加鎖pthread_mutex_lock()、解鎖pthread_mutex_unlock()和測試加鎖pthread_mutex_trylock()三個,不論哪種類型的鎖,都不可能被兩個不同的線程同時得到,而必須等待解鎖。對于普通鎖和適應鎖類型,解鎖者可以是同進程內任何線程;而檢錯鎖則必須由加鎖者解鎖才有效,否則返回EPERM;對于嵌套鎖,文檔和實現要求必須由加鎖者解鎖,但實驗結果表明并沒有這種限制,這個不同目前還沒有得到解釋。在同一進程中的線程,如果加鎖后沒有解鎖,則任何其他線程都無法再獲得鎖。

      int pthread_mutex_lock(pthread_mutex_t *mutex)

      int pthread_mutex_unlock(pthread_mutex_t *mutex)

      int pthread_mutex_trylock(pthread_mutex_t *mutex)

      pthread_mutex_trylock()語義與pthread_mutex_lock()類似,不同的是在鎖已經被占據時返回EBUSY而不是掛起等待。

      4. 其他

      POSIX線程鎖機制的Linux實現都不是取消點,因此,延遲取消類型的線程不會因收到取消信號而離開加鎖等待。值得注意的是,如果線程在加鎖后解鎖前被取消,鎖將永遠保持鎖定狀態,因此如果在關鍵區段內有取消點存在,或者設置了異步取消類型,則必須在退出回調函數中解鎖。

      這個鎖機制同時也不是異步信號安全的,也就是說,不應該在信號處理過程中使用互斥鎖,否則容易造成死鎖。

      二.條件變量

      條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起;另一個線程使"條件成立"(給出條件成立信號)。為了防止競爭,條件變量的使用總是和一個互斥鎖結合在一起。

      1. 創建和注銷

      條件變量和互斥鎖一樣,都有靜態動態兩種創建方式,靜態方式使用PTHREAD_COND_INITIALIZER常量,如下:

      pthread_cond_t cond=PTHREAD_COND_INITIALIZER

      動態方式調用pthread_cond_init()函數,API定義如下:

      int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

      盡管POSIX標準中為條件變量定義了屬性,但在LinuxThreads中沒有實現,因此cond_attr值通常為NULL,且被忽略。

      注銷一個條件變量需要調用pthread_cond_destroy(),只有在沒有線程在該條件變量上等待的時候才能注銷這個條件變量,否則返回EBUSY。因為Linux實現的條件變量沒有分配什么資源,所以注銷動作只包括檢查是否有等待線程。API定義如下:

      int pthread_cond_destroy(pthread_cond_t *cond)

      2. 等待和激發

      int pthread_cond_wait(pthread_cond_t *cond, ? pthread_mutex_t *mutex)

      int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t ? *mutex, const struct timespec *abstime)

      等待條件有兩種方式:無條件等待pthread_cond_wait()和計時等待pthread_cond_timedwait(),其中計時等待方式如果在給定時刻前條件沒有滿足,則返回ETIMEOUT,結束等待,其中abstime以與time()系統調用相同意義的絕對時間形式出現,0表示格林尼治時間1970年1月1日0時0分0秒。

      無論哪種等待方式,都必須和一個互斥鎖配合,以防止多個線程同時請求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的競爭條件(Race Condition)。mutex互斥鎖必須是普通鎖(PTHREAD_MUTEX_TIMED_NP)或者適應鎖(PTHREAD_MUTEX_ADAPTIVE_NP),且在調用pthread_cond_wait()前必須由本線程加鎖(pthread_mutex_lock()),而在更新條件等待隊列以前,mutex保持鎖定狀態,并在線程掛起進入等待前解鎖。在條件滿足從而離開pthread_cond_wait()之前,mutex將被重新加鎖,以與進入pthread_cond_wait()前的加鎖動作對應。

      激發條件有兩種形式,pthread_cond_signal()激活一個等待該條件的線程,存在多個等待線程時按入隊順序激活其中一個;而pthread_cond_broadcast()則激活所有等待線程。

      3. 其他

      pthread_cond_wait()和pthread_cond_timedwait()都被實現為取消點,因此,在該處等待的線程將立即重新運行,在重新鎖定mutex后離開pthread_cond_wait(),然后執行取消動作。也就是說如果pthread_cond_wait()被取消,mutex是保持鎖定狀態的,因而需要定義退出回調函數來為其解鎖。

      以下示例集中演示了互斥鎖和條件變量的結合使用,以及取消對于條件等待動作的影響。在例子中,有兩個線程被啟動,并等待同一個條件變量,如果不使用退出回調函數(見范例中的注釋部分),則tid2將在pthread_mutex_lock()處永久等待。如果使用回調函數,則tid2的條件等待及主線程的條件激發都能正常工作。

      #include #include #include ?pthread_mutex_t mutex;pthread_cond_t? cond;?void * child1(void *arg){??????? pthread_cleanup_push(pthread_mutex_unlock,&mutex);? /* comment 1 */??????? while(1){??????????????? printf("thread 1 get running \n");??????? printf("thread 1 pthread_mutex_lock returns %d\n",pthread_mutex_lock(&mutex));??????? pthread_cond_wait(&cond,&mutex);??????????????????? printf("thread 1 condition applied\n");??????? pthread_mutex_unlock(&mutex);??????????????????? sleep(5);??? }??????? pthread_cleanup_pop(0);???? /* comment 2 */}?void *child2(void *arg){??????? while(1){??????????????? sleep(3);?????????????? /* comment 3 */??????????????? printf("thread 2 get running.\n");??????? printf("thread 2 pthread_mutex_lock returns %d\n",pthread_mutex_lock(&mutex));??????? pthread_cond_wait(&cond,&mutex);??????? printf("thread 2 condition applied\n");??????? pthread_mutex_unlock(&mutex);??????? sleep(1);??????? }}?int main(void){??????? int tid1,tid2;???????? printf("hello, condition variable test\n");??????? pthread_mutex_init(&mutex,NULL);??????? pthread_cond_init(&cond,NULL);??????? pthread_create(&tid1,NULL,child1,NULL);??????? pthread_create(&tid2,NULL,child2,NULL);??????? do{??????? sleep(2);?????????????????? /* comment 4 */??????????????? pthread_cancel(tid1);?????? /* comment 5 */??????????????? sleep(2);?????????????????? /* comment 6 */??????? pthread_cond_signal(&cond);??? }while(1);? ????????sleep(100);??????? pthread_exit(0);}

      如果不做注釋5的pthread_cancel()動作,即使沒有那些sleep()延時操作,child1和child2都能正常工作。注釋3和注釋4的延遲使得child1有時間完成取消動作,從而使child2能在child1退出之后進入請求鎖操作。如果沒有注釋1和注釋2的回調函數定義,系統將掛起在child2請求鎖的地方;而如果同時也不做注釋3和注釋4的延時,child2能在child1完成取消動作以前得到控制,從而順利執行申請鎖的操作,但卻可能掛起在pthread_cond_wait()中,因為其中也有申請mutex的操作。child1函數給出的是標準的條件變量的使用方式:回調函數保護,等待條件前鎖定,pthread_cond_wait()返回后解鎖。

      條件變量機制不是異步信號安全的,也就是說,在信號處理函數中調用pthread_cond_signal()或者pthread_cond_broadcast()很可能引起死鎖。

      三.信號燈

      信號燈與互斥鎖和條件變量的主要不同在于"燈"的概念,燈亮則意味著資源可用,燈滅則意味著不可用。如果說后兩中同步方式側重于"等待"操作,即資源不可用的話,信號燈機制則側重于點燈,即告知資源可用;沒有等待線程的解鎖或激發條件都是沒有意義的,而沒有等待燈亮的線程的點燈操作則有效,且能保持燈亮狀態。當然,這樣的操作原語也意味著更多的開銷。

      信號燈的應用除了燈亮/燈滅這種二元燈以外,也可以采用大于1的燈數,以表示資源數大于1,這時可以稱之為多元燈。

      1. 創建和注銷

      POSIX信號燈標準定義了有名信號燈和無名信號燈兩種,但LinuxThreads的實現僅有無名燈,同時有名燈除了總是可用于多進程之間以外,在使用上與無名燈并沒有很大的區別,因此下面僅就無名燈進行討論。

      int sem_init(sem_t *sem, int ? pshared, unsigned int value)

      這是創建信號燈的API,其中value為信號燈的初值,pshared表示是否為多進程共享而不僅僅是用于一個進程。LinuxThreads沒有實現多進程共享信號燈,因此所有非0值的pshared輸入都將使sem_init()返回-1,且置errno為ENOSYS。初始化好的信號燈由sem變量表征,用于以下點燈、滅燈操作。

      int sem_destroy(sem_t * sem)

      被注銷的信號燈sem要求已沒有線程在等待該信號燈,否則返回-1,且置errno為EBUSY。除此之外,LinuxThreads的信號燈注銷函數不做其他動作。

      2. 點燈和滅燈

      int sem_post(sem_t * sem)

      點燈操作將信號燈值原子地加1,表示增加一個可訪問的資源。

      int sem_wait(sem_t * sem)

      int sem_trywait(sem_t * sem)

      sem_wait()為等待燈亮操作,等待燈亮(信號燈值大于0),然后將信號燈原子地減1,并返回。sem_trywait()為sem_wait()的非阻塞版,如果信號燈計數大于0,則原子地減1并返回0,否則立即返回-1,errno置為EAGAIN。

      3. 獲取燈值

      int sem_getvalue(sem_t * sem, int * sval)

      讀取sem中的燈計數,存于*sval中,并返回0。

      4. 其他

      sem_wait()被實現為取消點,而且在支持原子"比較且交換"指令的體系結構上,sem_post()是唯一能用于異步信號處理函數的POSIX異步信號安全的API。

      四.異步信號

      由于LinuxThreads是在核外使用核內輕量級進程實現的線程,所以基于內核的異步信號操作對于線程也是有效的。但同時,由于異步信號總是實際發往某個進程,所以無法實現POSIX標準所要求的"信號到達某個進程,然后再由該進程將信號分發到所有沒有阻塞該信號的線程中"原語,而是只能影響到其中一個線程。

      POSIX異步信號同時也是一個標準C庫提供的功能,主要包括信號集管理(sigemptyset()、sigfillset()、sigaddset()、sigdelset()、sigismember()等)、信號處理函數安裝(sigaction())、信號阻塞控制(sigprocmask())、被阻塞信號查詢(sigpending())、信號等待(sigsuspend())等,它們與發送信號的kill()等函數配合就能實現進程間異步信號功能。LinuxThreads圍繞線程封裝了sigaction()何raise(),本節集中討論LinuxThreads中擴展的異步信號函數,包括pthread_sigmask()、pthread_kill()和sigwait()三個函數。毫無疑問,所有POSIX異步信號函數對于線程都是可用的。

      int pthread_sigmask(int how, ? const sigset_t *newmask, sigset_t *oldmask)

      設置線程的信號屏蔽碼,語義與sigprocmask()相同,但對不允許屏蔽的Cancel信號和不允許響應的Restart信號進行了保護。被屏蔽的信號保存在信號隊列中,可由sigpending()函數取出。

      int pthread_kill(pthread_t ? thread, int signo)

      向thread號線程發送signo信號。實現中在通過thread線程號定位到對應進程號以后使用kill()系統調用完成發送。

      int sigwait(const sigset_t *set, ? int *sig)

      掛起線程,等待set中指定的信號之一到達,并將到達的信號存入*sig中。POSIX標準建議在調用sigwait()等待信號以前,進程中所有線程都應屏蔽該信號,以保證僅有sigwait()的調用者獲得該信號,因此,對于需要等待同步的異步信號,總是應該在創建任何線程以前調用pthread_sigmask()屏蔽該信號的處理。而且,調用sigwait()期間,原來附接在該信號上的信號處理函數不會被調用。

      如果在等待期間接收到Cancel信號,則立即退出等待,也就是說sigwait()被實現為取消點。

      五.其他同步方式

      除了上述討論的同步方式以外,其他很多進程間通信手段對于LinuxThreads也是可用的,比如基于文件系統的IPC(管道、Unix域Socket等)、消息隊列(Sys.V或者Posix的)、System ? V的信號燈等。只有一點需要注意,LinuxThreads在核內是作為共享存儲區、共享文件系統屬性、共享信號處理、共享文件描述符的獨立進程看待的。

      本文是第四篇將向您講述線程中止。

      1.線程終止方式

      一般來說,Posix的線程終止有兩種情況:正常終止和非正常終止。線程主動調用pthread_exit()或者從線程函數中return都將使線程正常退出,這是可預見的退出方式;非正常終止是線程在其他線程的干預下,或者由于自身運行出錯(比如訪問非法地址)而退出,這種退出方式是不可預見的。

      2.線程終止時的清理

      不論是可預見的線程終止還是異常終止,都會存在資源釋放的問題,在不考慮因運行出錯而退出的前提下,如何保證線程終止時能順利的釋放掉自己所占用的資源,特別是鎖資源,就是一個必須考慮解決的問題。

      最經常出現的情形是資源獨占鎖的使用:線程為了訪問臨界資源而為其加上鎖,但在訪問過程中被外界取消,如果線程處于響應取消狀態,且采用異步方式響應,或者在打開獨占鎖以前的運行路徑上存在取消點,則該臨界資源將永遠處于鎖定狀態得不到釋放。外界取消操作是不可預見的,因此的確需要一個機制來簡化用于資源釋放的編程。

      在POSIX線程API中提供了一個pthread_cleanup_push()/pthread_cleanup_pop()函數對用于自動釋放資源--從pthread_cleanup_push()的調用點到pthread_cleanup_pop()之間的程序段中的終止動作(包括調用pthread_exit()和取消點終止)都將執行pthread_cleanup_push()所指定的清理函數。API定義如下:

      void pthread_cleanup_push(void (*routine) (void? *),? ? void *arg)

      void pthread_cleanup_pop(int execute)

      pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的棧結構管理,void routine(void *arg)函數在調用pthread_cleanup_push()時壓入清理函數棧,多次對pthread_cleanup_push()的調用將在清理函數棧中形成一個函數鏈,在執行該函數鏈時按照壓棧的相反順序彈出。execute參數表示執行到pthread_cleanup_pop()時是否在彈出清理函數的同時執行該函數,為0表示不執行,非0為執行;這個參數并不影響異常終止時清理函數的執行。

      pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式實現的,這是pthread.h中的宏定義:

      #define pthread_cleanup_push(routine,arg)????????????????????????????? ???????\

      { struct ? _pthread_cleanup_buffer _buffer;?????????????????????????????????? \

      _pthread_cleanup_push (&_buffer, (routine), (arg));

      #define pthread_cleanup_pop(execute)????????????????????????????????????????? \

      _pthread_cleanup_pop (&_buffer, (execute)); }

      可見,pthread_cleanup_push()帶有一個"{",而pthread_cleanup_pop()帶有一個"}",因此這兩個函數必須成對出現,且必須位于程序的同一級別的代碼段中才能通過編譯。在下面的例子里,當線程在"do some work"中終止時,將主動調用pthread_mutex_unlock(mut),以完成解鎖動作。

      pthread_cleanup_push(pthread_mutex_unlock, (void *) ? &mut);

      pthread_mutex_lock(&mut);

      /* do some work */

      pthread_mutex_unlock(&mut);

      pthread_cleanup_pop(0);

      必須要注意的是,如果線程處于PTHREAD_CANCEL_ASYNCHRONOUS狀態,上述代碼段就有可能出錯,因為CANCEL事件有可能在pthread_cleanup_push()和pthread_mutex_lock()之間發生,或者在pthread_mutex_unlock()和pthread_cleanup_pop()之間發生,從而導致清理函數unlock一個并沒有加鎖的mutex變量,造成錯誤。因此,在使用清理函數的時候,都應該暫時設置成PTHREAD_CANCEL_DEFERRED模式。為此,POSIX的Linux實現中還提供了一對不保證可移植的pthread_cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()擴展函數,功能與以下代碼段相當:

      { int oldtype;

      pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, ? &oldtype);

      pthread_cleanup_push(routine, arg);

      ...

      pthread_cleanup_pop(execute);

      pthread_setcanceltype(oldtype, NULL);

      }

      3.線程終止的同步及其返回值

      一般情況下,進程中各個線程的運行都是相互獨立的,線程的終止并不會通知,也不會影響其他線程,終止的線程所占用的資源也并不會隨著線程的終止而得到釋放。正如進程之間可以用wait()系統調用來同步終止并釋放資源一樣,線程之間也有類似機制,那就是pthread_join()函數。

      void pthread_exit(void *retval)

      int pthread_join(pthread_t th, void **thread_return)

      int pthread_detach(pthread_t th)

      pthread_join()的調用者將掛起并等待th線程終止,retval是pthread_exit()調用者線程(線程ID為th)的返回值,如果thread_return不為NULL,則*thread_return=retval。需要注意的是一個線程僅允許唯一的一個線程使用pthread_join()等待它的終止,并且被等待的線程應該處于可join狀態,即非DETACHED狀態。

      如果進程中的某個線程執行了pthread_detach(th),則th線程將處于DETACHED狀態,這使得th線程在結束運行時自行釋放所占用的內存資源,同時也無法由pthread_join()同步,pthread_detach()執行之后,對th請求pthread_join()將返回錯誤。

      一個可join的線程所占用的內存僅當有線程對其執行了pthread_join()后才會釋放,因此為了避免內存泄漏,所有線程的終止,要么已設為DETACHED,要么就需要使用pthread_join()來回收。

      4.關于pthread_exit()和return

      理論上說,pthread_exit()和線程宿體函數退出的功能是相同的,函數結束時會在內部自動調用pthread_exit()來清理線程相關的資源。但實際上二者由于編譯器的處理有很大的不同。

      在進程主函數(main())中調用pthread_exit(),只會使主函數所在的線程(可以說是進程的主線程)退出;而如果是return,編譯器將使其調用進程退出的代碼(如_exit()),從而導致進程及其所有線程結束運行。

      其次,在線程宿主函數中主動調用return,如果return語句包含在pthread_cleanup_push()/pthread_cleanup_pop()對中,則不會引起清理函數的執行,反而會導致segment fault。

      在Posix線程規范中還有幾個輔助函數難以歸類,暫且稱其為雜項函數,主要包括pthread_self()、pthread_equal()和pthread_once()三個,另外還有一個LinuxThreads非可移植性擴展函數pthread_kill_other_threads_np()。本文就介紹這幾個函數的定義和使用。

      1.獲得本線程ID

      pthread_t pthread_self(void)

      本函數返回本線程的標識符。

      在LinuxThreads中,每個線程都用一個pthread_descr結構來描述,其中包含了線程狀態、線程ID等所有需要的數據結構,此函數的實現就是在線程棧幀中找到本線程的pthread_descr結構,然后返回其中的p_tid項。

      pthread_t類型在LinuxThreads中定義為無符號長整型。

      2.判斷兩個線程是否為同一線程

      int pthread_equal(pthread_t thread1, pthread_t thread2)

      判斷兩個線程描述符是否指向同一線程。在LinuxThreads中,線程ID相同的線程必然是同一個線程,因此,這個函數的實現僅僅判斷thread1和thread2是否相等。

      3.僅執行一次的操作

      int pthread_once(pthread_once_t *once_control, void (*init_routine) (void))

      本函數使用初值為PTHREAD_ONCE_INIT的once_control變量保證init_routine()函數在本進程執行序列中僅執行一次。

      #include

      #include

      pthread_once_t? ? once=PTHREAD_ONCE_INIT;

      void??? ? once_run(void)

      {

      printf("once_run in thread %d\n",pthread_self());

      }

      void * child1(void *arg)

      {

      int ? tid=pthread_self();

      printf("thread %d enter\n",tid);

      pthread_once(&once,once_run);

      printf("thread %d returns\n",tid);

      }

      void * child2(void *arg)

      {

      int ? tid=pthread_self();

      printf("thread %d enter\n",tid);

      pthread_once(&once,once_run);

      printf("thread %d returns\n",tid);

      }

      int main(void)

      {

      int ? tid1,tid2;

      printf("hello\n");

      pthread_create(&tid1,NULL,child1,NULL);

      pthread_create(&tid2,NULL,child2,NULL);

      sleep(10);

      printf("main thread exit\n");

      return 0;

      }

      once_run()函數僅執行一次,且究竟在哪個線程中執行是不定的,盡管pthread_once(&once,once_run)出現在兩個線程中。

      LinuxThreads使用互斥鎖和條件變量保證由pthread_once()指定的函數執行且僅執行一次,而once_control則表征是否執行過。如果once_control的初值不是PTHREAD_ONCE_INIT(LinuxThreads定義為0),pthread_once()的行為就會不正常。在LinuxThreads中,實際"一次性函數"的執行狀態有三種:NEVER(0)、IN_PROGRESS(1)、DONE(2),如果once初值設為1,則由于所有pthread_once()都必須等待其中一個激發"已執行一次"信號,因此所有pthread_once()都會陷入永久的等待中;如果設為2,則表示該函數已執行過一次,從而所有pthread_once()都會立即返回0。

      4. pthread_kill_other_threads_np()

      void pthread_kill_other_threads_np(void)

      這個函數是LinuxThreads針對本身無法實現的POSIX約定而做的擴展。POSIX要求當進程的某一個線程執行exec*系統調用在進程空間中加載另一個程序時,當前進程的所有線程都應終止。由于LinuxThreads的局限性,該機制無法在exec中實現,因此要求線程執行exec前手工終止其他所有線程。pthread_kill_other_threads_np()的作用就是這個。

      需要注意的是,pthread_kill_other_threads_np()并沒有通過pthread_cancel()來終止線程,而是直接向管理線程發"進程退出"信號,使所有其他線程都結束運行,而不經過Cancel動作,當然也不會執行退出回調函數。盡管LinuxThreads的實驗結果與文檔說明相同,但代碼實現中卻是用的__pthread_sig_cancel信號來kill線程,應該效果與執行pthread_cancel()是一樣的,其中原因目前還不清楚。

      任務調度 機器學習

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:類變量基本使用
      下一篇:淺談java多線程的生命周期和同步通信多線程的生命周期和同步通信
      相關文章
      亚洲熟妇无码八V在线播放 | 亚洲GV天堂无码男同在线观看| 国产亚洲精久久久久久无码AV| 亚洲精品国产精品乱码不99| 国产成人精品日本亚洲直接 | 亚洲国产成人va在线观看网址| 久久亚洲sm情趣捆绑调教| 亚洲AV无码专区在线播放中文| 日韩一卡2卡3卡4卡新区亚洲 | 亚洲熟妇无码AV| 亚洲熟女www一区二区三区| 亚洲一区二区三区在线观看网站| 亚洲综合色丁香婷婷六月图片| 亚洲日本乱码卡2卡3卡新区| 亚洲色图黄色小说| 久久精品亚洲综合一品| 久久久亚洲精品视频| 亚洲福利视频一区| 亚洲系列国产精品制服丝袜第| 亚洲精品第五页中文字幕| 亚洲国产精品成人久久久| 精品日韩亚洲AV无码| 亚洲成aⅴ人片在线观| 亚洲一区二区三区免费视频| 中文字幕亚洲精品无码| 亚洲av无码专区首页| mm1313亚洲精品无码又大又粗| 亚洲AV无码乱码在线观看代蜜桃| 国产成人亚洲精品| 亚洲熟女乱色一区二区三区| 国产偷国产偷亚洲清高APP| 亚洲国产精品一区二区第四页| 亚洲中文字幕无码一久久区| 久久亚洲精品成人777大小说| 亚洲黄色网站视频| 国产精品亚洲综合久久| 小说区亚洲自拍另类| 中文字幕中韩乱码亚洲大片| 亚洲AV成人精品网站在线播放| 亚洲精品国产啊女成拍色拍| 欧洲 亚洲 国产图片综合|