可連接和分離的線程 | Joinable and Detached Threads

本文是對(duì)《Joinable And Detached Threads -- by Lo?c》的簡(jiǎn)體中文的翻譯版本。

介紹

默認(rèn)情況下,創(chuàng)建的線程即是可連接的(或可結(jié)合的,joinable)。這意味著我們可以使用pthread_join()函數(shù)在任何其它線程中等待它(可連接線程)的終止:

#include <pthread.h>
int pthread_join(
    pthread_t thread,  //thread to join
    void **value_ptr   //store value returned by thread
);

這個(gè)函數(shù)將阻塞調(diào)用線程,直到目標(biāo)線程thread終止。當(dāng)value_ptr非空時(shí),其中將包含著由thread線程返回的信息。在我們的文章POSIX線程參數(shù)傳遞 | Pthreads argument passing中看到了如何使用pthread_join()函數(shù)。

可連接的線程及其結(jié)果

一個(gè)線程在我們連接(join)它之前可能就已經(jīng)終止了。結(jié)果是,當(dāng)一個(gè)線程是可連接時(shí),POSIX線程系統(tǒng)必須保持、維護(hù)某些信息:至少是該線程的ID和返回值(the returned value)[1]。的確,正在等待連接該線程的線程是需要這些信息的。
實(shí)際上,POSIX線程系統(tǒng)也可保持和可連接線程相關(guān)的其它資源信息,比如線程的棧。從POSIX的角度來(lái)看,這是完全合法的。事實(shí)上,pthread_join()保證回收與已連接線程的任何存儲(chǔ)空間。

Pthreads的內(nèi)部實(shí)現(xiàn)

維護(hù)整個(gè)線程的??赡軆H僅幾個(gè)字節(jié)被確切需要,這看起來(lái)像是對(duì)系統(tǒng)資源的極大浪費(fèi)(在一個(gè)64位的架構(gòu)上,線程ID和返回值信息加起來(lái)一般也只需要16個(gè)字節(jié))。然而,一些Pthreads實(shí)現(xiàn)使用這種可能性來(lái)簡(jiǎn)化和優(yōu)化線程管理。實(shí)際上,線程實(shí)現(xiàn)需要存儲(chǔ)和管理每個(gè)線程特有的屬性,比如:

  • 線程ID,線程屬性,起始入口函數(shù),參數(shù)及返回值;
  • 線程的調(diào)度策略和優(yōu)先級(jí);
  • 信號(hào)掩碼,備用信號(hào)棧;
  • 用于取消、清理緩存的標(biāo)志;
  • 用于線程私有數(shù)據(jù)的鍵;
  • 錯(cuò)誤編號(hào);
  • ... ...

讓我們稱線程的這種管理存儲(chǔ)為線程控制塊(the Thread Control Block, TCB)。一種常見(jiàn)的技術(shù)是一次性分配堆棧和TCB(比如使用單次mmap()調(diào)用),并把TCB放在棧的開(kāi)始位置處[2]。

在這樣類似的架構(gòu)上,我們不能在沒(méi)有回收TCB的情況下回收整個(gè)棧。這就是問(wèn)題的關(guān)鍵所在:對(duì)于連接操作所需要的信息是TCB的一部分,因此整個(gè)??臻g都需要被維持。眾所周知,在Linux、AIX、Sloaris等的Pthreads的實(shí)現(xiàn)中都采用這種技術(shù)。

分離線程

那當(dāng)我的線程不返回任何有用的信息,并且我不必等待它的完成時(shí),我該怎么辦??jī)H僅為了清理的意圖,無(wú)論如何還是得必須調(diào)用pthrad_join()嗎?
幸運(yùn)的是,不必這樣做。Pthreads提供了一種機(jī)制告訴管理系統(tǒng):我正在開(kāi)啟這個(gè)線程,但我對(duì)連接它并沒(méi)有興趣;一旦該線程結(jié)束,請(qǐng)為我執(zhí)行清理動(dòng)作。這個(gè)操作叫做分離一個(gè)線程。我們可以按照如下方法分離一個(gè)線程:

  • 在線程的創(chuàng)建期間,采用detachstate線程屬性
  • 在任何線程處調(diào)用pthread_detach()函數(shù)

讓我們先看一下第二種形式,也是最簡(jiǎn)單的:

#include <pthread.h>
int pthread_detach(
    pthread_t thread  //thread to detach
);

函數(shù)pthread_detach()可以被任何線程調(diào)用,特別是從線程內(nèi)分離(即自家線程調(diào)用,通過(guò)pthread_self() API可以獲得它自己的線程ID)。
要想以分離狀態(tài)創(chuàng)建一個(gè)線程,我們可以通過(guò)pthread_attr_setdetachstate()函數(shù)設(shè)置detachstate線程屬性為PTHREAD_CREATE_DETACHED。如下所示:

#include <pthread.h>
#
pthread_t      tid;  // thread ID
pthread_attr_t attr; // thread attribute
#
// set thread detachstate attribute to DETACHED
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
#
// create the thread 
pthread_create(&tid, &attr, start_routine, arg);
...

下述程序闡明了分離一個(gè)線程的兩種方法。

/*------------------------------- join_01.c --------------------------------*
On Linux, compile with: 
cc -std=c99 -pthread join_01.c -o join_01 
 #
Check your system documentation how to enable C99 and POSIX threads on 
other Un*x systems.
 #
Copyright Loic Domaigne. 
Licensed under the Apache License, Version 2.0.
*--------------------------------------------------------------------------*/
 #
#include <unistd.h>  // sleep()
#include <pthread.h> 
#include <stdio.h>   
#include <stdlib.h>  // EXIT_SUCCESS
#include <string.h>  // strerror() 
#include <errno.h>
 #
/***************************************************************************/
/* our macro for errors checking                                           */
/***************************************************************************/
#define COND_CHECK(func, cond, retv, errv) \
if ( (cond) ) \
{ \
   fprintf(stderr, "\n[CHECK FAILED at %s:%d]\n| %s(...)=%d (%s)\n\n",\
              __FILE__,__LINE__,func,retv,strerror(errv)); \
   exit(EXIT_FAILURE); \
}
 #
#define ErrnoCheck(func,cond,retv)  COND_CHECK(func, cond, retv, errno)
#define PthreadCheck(func,rc) COND_CHECK(func,(rc!=0), rc, rc)
 #
/*****************************************************************************/
/* thread- dummy thread                                                      */
/*****************************************************************************/
void*
thread(void* ignore)
{
   sleep(1);
   return NULL;
}
 #
/*****************************************************************************/
/* detach_state. Print detachstate of a thread.                              */
/*****************************************************************************/
/* 
 * We find out indirectly if a thread is detached using pthread_join().  
 * If a thread is detached, then pthread_join() fails with EINVAL. 
 * Otherwise the thread is joined, and hence was joinable. 
 *
 */
void
detach_state(
   pthread_t   tid,  // thread to check detach status
   const char *tname // thread name
   )
{
   int rc; // return code
 #
   rc = pthread_join(tid, NULL);
   if ( rc==EINVAL ) 
   {
      printf("%s is detached\n", tname);
   }
   else if ( rc==0 )  
   {  
      printf("%s was joinable\n", tname);
   }
   else 
   {
      printf("%s: pthread_join() = %d (%s)\n", 
             tname, rc, strerror(rc)
            );
   }
}
 #
/*****************************************************************************/
/* main- main thread                                                         */
/*****************************************************************************/
int
main()
{
   pthread_t tid1, tid2, tid3; // thread 1,2 and 3.
   pthread_attr_t attr;        // thread's attribute
   int rc;  // return code
 #
   /*--------------------------------------------------------*/
   /* 1st test: normal thread creation                       */
   /*--------------------------------------------------------*/
   rc = pthread_create(&tid1, NULL, thread, NULL);
   PthreadCheck("pthread_create", rc);
   detach_state(tid1, "thread1"); // expect: joinable 
 #
   /*--------------------------------------------------------*/
   /* 2nd test: detach thread from main thread               */
   /*--------------------------------------------------------*/
   rc = pthread_create(&tid2, NULL, thread, NULL);
   PthreadCheck("pthread_create", rc);
   rc = pthread_detach(tid2);
   PthreadCheck("pthread_detach", rc);
   detach_state(tid2, "thread2"); // expect: detached
 #
   /*--------------------------------------------------------*/
   /* 3rd test: create detached thread                       */
   /*--------------------------------------------------------*/
 #
   // set detachstate attribute to DETACHED
   //
   rc=pthread_attr_init(&attr);
   PthreadCheck("pthread_attr_init", rc);
   rc=pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   PthreadCheck("pthread_attr_setdetachstate", rc);
 #
   // create thread now 
   //
   rc = pthread_create(&tid3, &attr, thread, NULL);
   PthreadCheck("pthread_create", rc);
   detach_state(tid3, "thread3");
 #
   /*--------------------------------------------------------*/
   /* that's all folks!                                      */
   /*--------------------------------------------------------*/
   return EXIT_SUCCESS;
}

對(duì)于連接或分離最重要的規(guī)則就是:

  • 不要連接一個(gè)已經(jīng)被連接的線程;(注:已連接的線程??臻g已被收回,再次連接將得不到可連接線程的信息)
  • 不要連接一個(gè)分離線程;(注:連接操作只可用于可連接的線程,因?yàn)榉蛛x線程??臻g的收回是由系統(tǒng)內(nèi)部來(lái)做的)
  • 如果你分離一個(gè)線程,你不能“重連接”它;(注:調(diào)用pthread_detach()后,可連接線程的??臻g已被收回,無(wú)法再恢復(fù))

Notes and further Reading

  • [1] pthread & detach. A post on c.p.t. where Patrick TJ MacPhee explains that (in theory) only the thread ID and returned value should be retained.
  • [2] pthread_join and thread stack. A post on c.p.t. where Paul Pluzhnikov explains why the stack is held until the thread is joined.
  • The main thread. Article where we discuss the special semantic of the main thread.
  • Pthreads argument passing. Article where we discuss how to pass arguments between threads.
  • David R. Butenhof: Programming with POSIX Threads, Addison-Wesley, ISBN-13 978-0-201-63392-4. See in particular Section 2.1, pp 35-39 and section 5.2.3, pp 138-141.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容