linux多線程編程學習心得

來源:互聯網
上載者:User

 

網上有一篇《Linux下的多線程編程》介紹的比較詳細,細讀了一遍,頗有收穫!

linux下的多執行緒模式:

可用getconf -a |
grep GNU_LIBPTHREAD_VERSION查看,我的機器用的是redhat公司研發的NPTL 2.3.4。

 

pthread實現的簡單說明:

pthread的實現通過系統調用clone()來實現。順便說一下fork/vfork/clone三者的區別(底層均調用do_fork函數):

 

1. fork:完全複製,深度拷貝,使用copy-on-write技術。父子進程同時執行,均不阻塞。“是unix核心建立一個新進程的唯一方法(不包括交換進程、init進程和頁精靈進程,這些進程是由核心作為自舉過程的一部分以特殊方式建立的)。”----摘自《unix進階環境編程8.3節》

 

2. vfork:共用地址空間,子進程先運行,直到它調用exit或者exec後父進程才運行。

 

3. clone:定製性建立進程(或線程)。linux下的pthread就是通過系統調用clone來實現的。共用記憶體(CLONE_VM)、檔案系統訪問計數(CLONE_FS)、檔案描述符表(CLONE_FILES)、訊號處理方式(CLONE_SIGHAND)。

pthread函數族

很多函數直接man看一下就知道函數是幹嘛的,有什麼用,這裡我只揀幾個說說:

 

1.pthread_exit 和pthread_join合用,取得子線程的返回碼:


線程中止,可以:線上程函數中return;被同一進程中的另外線程cancel掉;線程調用pthread_exit函數。

 

1.1 線程函數return:

A* fun(){A* a = new A(); return a;}<br />A* a;<br />pthread_join(pthread,(void**)(&a)); 

1.2 被cancel掉:

調用pthread_cancel,非阻塞調用,應該是向核心一級發送cancel訊號。如何處理cancel訊號,則由目標線程自己決定:立即終止、忽略、繼續運行至cancel_point(預設)。

根 據POSIX標準,pthread_join()、pthread_testcancel()、pthread_cond_wait()、
pthread_cond_timedwait()、sem_wait()、sigwait()等函數以及read()、write()等會引起阻塞的系 統調用都是Cancelation-point,而其他pthread函數都不會引起Cancelation動作。但是pthread_cancel的手 冊頁聲稱,由於LinuxThread庫與C庫結合得不好,因而目前C庫函數都不是Cancelation-point;CANCEL訊號會使線程從阻 塞的系統調用中退出,共置EINTR錯誤碼,因此可以在需要作為Cancelation-point的系統調用前後調用 pthread_testcancel(),從而達到POSIX標準所要求的目標,即如下程式碼片段:

pthread_testcancel();    retcode = read(fd, buffer, length);    pthread_testcancel();


如果被cancel掉,pthread_join得到的傳回值就是PTHREAD_CANCELLED(-1)


void* fun(){pthread_testcancel();}<br />pthread_cancel(pth);<br />int* p = new int;<br />pthread_join(pth, (void**)(p)); 

1.3 調用pthread_exit


pthread_exit((void*)10);



2. 線程建立時的屬性設定pthread_create第二個參數


pthread_attr_t:主要包括範圍、是否分離、堆棧地址、堆棧大小、優先順序等。

 

typedef struct<br />{<br /> int detachstate; 線程的分離狀態<br /> int schedpolicy; 線程調度策略<br /> struct sched_param schedparam; 線程的調度參數<br /> int inheritsched; 線程的繼承性<br /> int scope; 線程的範圍<br /> size_t guardsize; 線程棧末尾的警戒緩衝區大小<br /> int stackaddr_set; 堆棧的地址集<br /> void * stackaddr; 線程棧的位置<br /> size_t stacksize; 線程棧的大小<br />}pthread_attr_t; (見多線程編程—線程屬性)

 

2.1 範圍:

 

pthread_attr_setscope(&attr,
PTHREAD_SCOPE_SYSTEM|PTHREAD_SCOPE_PROCESS);

這裡準確的理解,應該是定義線程爭奪資源的scope(系統級or進程級):The  contentionscope attribute may have the values
PTHREAD_SCOPE_SYSTEM, signifying system scheduling contention scope, or
PTHREAD_SCOPE_PROCESS, signifying process scheduling contention scope

 

2.2 是否分離:

 

線程的分離狀態,決定一個線程以什麼樣的方式終止自己。預設的非分離狀態(PTHREAD
_CREATE_JOINABLE)下,只有當pthread_join函數返回以後,建立線程才算終止,才能釋放自己佔用的系統資源,否則會造成記憶體的泄露。分離狀態(PTHREAD_CREATE_DETACHED)下,線程運行結束,立即釋放系統資源,沒有被其他線程等待。

注意:在分離狀態下,線程有可能在pthread_create函數返回前就結束,並且有可能將線程號和系統資源移交給其他線程,這樣,pthread_create返回得到的線程id就是錯誤的。可以使用線程同步機制來避免這種情況!

 

2.3 堆棧地址:

 

可以設定線程的堆棧在使用者自己申請的堆棧地址中運行。

 

2.4 堆棧大小:

 

ulimit
-a可以看到stack的大小限制,線上程屬性裡設定堆棧大小,可以突破這個限制。相應的,還有設定警戒緩衝區大小的pthread_attr_setguardsize函數。

 

2.5 優先順序(調度參數):


系統支援的最大和最小優先權值可以用sched_get_priority_max函數和sched_get_priority_min函數分別得到。如果不是編寫即時程式,不建議修改。因為,調度策略是一件非常複雜的事情,如果不正確使用會導致程式錯誤,從而導致死結等問題。如:在多線程應用程式中為線程設定不同的優先順序別,有可能因為共用資源而導致優先順序倒置。

 

3.pthread的TSD(線程特有資料)

 

線程的私人全域變數(同名不同地址)。僅在某個線程中有效,但可以跨多個函數使用。比如常見的errno就是一個TSD。pthread_key_delete用來刪除一個鍵,這個鍵佔用的記憶體將被釋放,但是,它並不釋放該鍵關聯的線程資料所佔用的資源,也不會觸發pthread_key_create中定義的destructor函數。詳見:《posix線程編程指南》

pthread執行個體分析

1、  記憶體泄露:

 

源碼:

char* url = new char[100];<br />int ret = pthread_create(&pt, NULL, sendurl, url);<br />void* sendurl(void*url)<br />{<br /> char* purl = (char*) url;<br /> // do something<br /> delete [] purl;<br /> return NULL;<br />} 

分析:很顯然,有記憶體泄露,既沒有設定線程的分離屬性,也沒有使用pthread_join等待線程結束。

 

解決:

 

可以設定線程的分離屬性:

pthread_t pt;<br />pthread_attr_t attr;<br />pthread_attr_init(&attr);<br />pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);<br />int ret = pthread_create(&pt, &attr, CntServer::SendUrl, url);<br />pthread_attr_destroy (&attr); 

 

或者實現自我分離:

線上程函數中pthread_detach(pthread_self());

注意:The effect of multiple
pthread_detach() calls on the same target thread is       unspecified.

 

或者使用pthread_join等待

pthread_join(pt,
NULL);


2、  對死迴圈的子線程進行停止操作

 

設定線程對cancel訊號的處理方式

pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,
NULL);      //允許退出線程  


設定立即取消或者延遲取消

 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,
NULL);        //設定立即取消

pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,
NULL);             //設定延遲取消

注意,延遲取消需要設定cancel_point(可增加pthread_testcancel)


使用pthread_cancel發送cancel訊號

pthread_cancel(pthread);

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.