Linux多線程總結

來源:互聯網
上載者:User

標籤:linux   多線程   

一、線程的理解

1、線程其實是一個進程的一個執行流。

2、線程是作業系統調度的基本單位,進程是承擔分配系統資源的基本單位。

3、linux下,一個進程就是一個獨佔資源的線程,即在這個地址空間僅有一個執行流,linux下的進程為輕量級進程(進程可以理解為是線程,可以理解為linux下均為線程),進程和線程均叫做pcb。

4、在一個進程中各線程還共用以下進程資源和環境:

1)檔案描述符表

2)每種訊號的處理方式式(SIG_IGN、 SIG_DFL或者定義的訊號處理函數)

3)當前工作目錄

4)使用者id和組id

5、在一個進程中各線程各有一份:

1)線程id

2)上下文,包括各種寄存器的值、程式計數器和棧指標

3)棧空間

4)errno變數

5)訊號屏蔽字
6.)調度優先順序

  在Linux上線程函數位於libpthread共用庫中,因此在編譯時間要加上-lpthread選項。

二、線程式控制制

1、建立線程

650) this.width=650;" src="https://s3.51cto.com/wyfs02/M02/A7/5B/wKioL1nlox3Cq64TAABZzjru9To871.png" title="thread.png" alt="wKioL1nlox3Cq64TAABZzjru9To871.png" />

  

  傳回值:成功返回0,失敗返回錯誤號碼。以前學過的系統函數都是成功返回0,失敗返回-1,而錯誤號碼儲存在全域變數errno中,而pthread庫的函數都是通過傳回值返回錯誤號碼,雖然每個線程也都有一個errno,但這是為了相容其它函數介面而提供的,pthread庫本身並不使用它,通過傳回值返回錯誤碼更加清晰。

  在一個線程中調用pthread_create()建立新的線程後,當前線程從pthread_create()返回繼續往下執行,而新的線程所執行的代碼由我們傳給pthread_create的函數指標start_routine決定。start_routine函數接收一個參數,是通過pthread_create的arg參數傳遞給它的,該參數的類型為void *,這個指標按什麼類型解釋由調用者自己定義。start_routine的傳回值類型也是void *,這個指標的含義同樣由調用者自己定義。start_routine返回時,這個線程就退出了,其它線程可以調用pthread_join得到函數start_routine的傳回值,類似於父進程調用wait(2)得到子進程的退出狀態。

  pthread_create成功返回後,新建立的線程的id被填寫到thread參數所指向的記憶體單元。調用getpid(2)可以獲得當前進程的id,是一個正整數值。線程id的類型是thread_t,它只在當前進程中保證是唯一的,在不同的系統中thread_t這個類型有不同的實現,它可能是一個整數值,也可能是一個結構體,也可能是一個地址,所以不能簡單地當成整數用printf列印,調用pthread_self(3)可以獲得當前線程的id。

#include<stdio.h>#include<stdlib.h>#include<pthread.h>pthread_t tid;void* thread_run(void *val){printf("%s : pid is :%d,tid is : %u\n",(char*)val,(int)getpid(),(unsigned long long)pthread_self());return NULL;}int main(){int err = pthread_create(&tid,NULL,thread_run,"other thread run");if( err != 0 ){printf("create thread error!info is :%s\n",strerror(err));}printf("main thread run : pid is :%d,tid is : %u\n",(int)getpid(),(unsigned long long)pthread_self());sleep(1);return 0;}


運行結果:

650) this.width=650;" src="https://s1.51cto.com/wyfs02/M01/A7/5C/wKioL1nlrAvhE9EeAABfoW_MVEo589.png" title="哈哈.png" alt="wKioL1nlrAvhE9EeAABfoW_MVEo589.png" />

     可知在Linux上,thread_t類型是一個地址值,屬於同一進程的多個線程調用getpid(2)可以得到相同的進程號,而調用pthread_self(3)得到的線程號各不相同。
  由於pthread_create的錯誤碼不儲存在errno中,因此不能直接用perror(3)列印錯誤資訊,可以先用strerror(3)把錯誤碼轉換成錯誤資訊再列印。
  如果任意一個線程調用了exit或_exit,則整個進程的所有線程都終止,由於從main函數return也相當於調用exit,為了防止新建立的線程還沒有得到執行就終止,我們在main函數return之前延時1秒,這隻是一種權宜之計,即使主線程等待1秒,核心也不一定會調度新建立的線程執行。

2、終止線程

如果需要只終止某個線程而不終止整個進程,可以有三種方法:
1) 從線程函數return。這種方法對主線程不適用,從main函數return相當於調用。

2)一個線程可以調用pthread_cancel終止同一進程中的另一個線程。
3)線程可以調用pthread_exit終止自己。

需要注意,pthread_exit或者return返回的指標所指向的記憶體單元必須是全域的或者是用malloc分配的,不能線上程函數的棧上分配,因為當其它線程得到這個返回指標時線程函數已經退出了。

3、線程等待

650) this.width=650;" src="https://s1.51cto.com/wyfs02/M00/A7/60/wKioL1nlwOiA5nT6AABIdAGFGQ4567.png" title="嘻嘻.png" alt="wKioL1nlwOiA5nT6AABIdAGFGQ4567.png" />

     傳回值:成功返回0,失敗返回錯誤號碼
  調用該函數的線程將掛起等待,直到id為thread的線程終止。 thread線程以不同的方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:
1)如果thread線程通過return返回,value_ptr所指向的單元裡存放的是thread線程函數的傳回值。
2)如果thread線程被別的線程調用pthread_cancel異常終掉,value_ptr所指向的單元裡存放的是常數PTHREAD_CANCELED。
3)如果thread線程是自己調用pthread_exit終止的,value_ptr所指向的單元存放的是傳給pthread_exit的參數。 如果對thread線程的終止狀態不感興趣,可以傳NULL給value_ptr參數。

#include<stdio.h>#include<stdlib.h>#include<pthread.h>void *thread1(void *val) {printf("thread 1 returning...\n");return (void*)1;}void *thread2(void *val){printf("thread 2 exiting...\n");pthread_exit((void*)2);}void *thread3(void *val){while(1){printf("pthread 3 is running,wait for be cancal...\n");sleep(1);}return NULL;}int main(){pthread_t tid;void *tret;pthread_create(&tid,NULL,thread1,NULL);pthread_join(tid,&tret);printf("thread1 return,thread1 id is:%u,return code is:%d\n",(unsigned long)tid,(int)tret);pthread_create(&tid,NULL,thread2,NULL);pthread_join(tid,&tret);printf("thread2 exit,thread2 id is:%u,exit code is:%d\n",(unsigned long)tid,(int)tret);    pthread_create(&tid,NULL,thread3,NULL);sleep(4);pthread_cancel(tid);pthread_join(tid,&tret);printf("thread3 return,thread3 id is:%u,cancal code is:%d\n",(unsigned long)tid,(int)tret);return 0;}


650) this.width=650;" src="https://s3.51cto.com/wyfs02/M02/A7/60/wKioL1nlwf3xqs-lAADE6CA6Hcc794.png" title="哈哈.png" alt="wKioL1nlwf3xqs-lAADE6CA6Hcc794.png" />

      可見在Linux的pthread庫中常數PTHREAD_CANCELED的值是-1。可以在標頭檔pthread.h中找到它的定義。

  一般情況下,線程終止後,其終止狀態一直保留到其它線程調用pthread_join擷取它的狀態為止。 但是線程也可以被置為detach狀態,這樣的線程一旦終止就立刻回收它佔用的所有資源,而不保留終止狀態。不能對一個已經處於detach狀態的線程調用pthread_join,這樣的調用將返回EINVAL。 對一個尚未detach的線程調用pthread_join或pthread_detach都可以把該線程置為detach狀態,也就是說,不能對同一線程調用兩次pthread_join,或者如果已經對一個線程調用了pthread_detach就不能再調用pthread_join了。

4、線程分離

  在任何一個時間點上, 線程是可結合的(joinable)或者是分離的(detached) 。 一個可結合的線程能夠被其他線程收回其資源和殺死。在被其他線程回收之前,它的儲存空間資源(例如棧)是不釋放的。 相反, 一個分離的線程是不能被其他線程回收或殺死的,它的儲存空間資源在它終止時由系統自動釋放。

  預設情況下,線程被建立成可結合的。 為了避免儲存空間泄漏,每個可結合線程都應該要麼被顯示地回收,即調用pthread_join;要麼通過調用pthread_detach函數被分離。如果一個可結合線程結束運行但沒有被join,則它的狀態類似於進程中的Zombie Process,即還有一部分資源沒有被回收,所以建立線程者應該調用pthread_join來等待線程運行結束,並可得到線程的結束代碼,回收其資源。
  由於調用pthread_join後,如果該線程沒有運行結束,調用者會被阻塞,在有些情況下我們並不希望如此。例如,在Web伺服器中當主線程為每個新來的串連請求建立一個子線程進行處理的時候,主線程並不希望因為調用pthread_join而阻塞(因為還要繼續處理之後到來的串連請求),這時可以在子線程中加入代碼:
pthread_detach(pthread_self())
或者父線程調用
pthread_detach(thread_id)(非阻塞,可立即返回)
這將該子線程的狀態設定為分離的(detached),如此一來,該線程運行結束後會自動釋放所有源。

#include<stdio.h>#include<stdlib.h>#include<pthread.h>void *thread(void *val){pthread_detach(pthread_self());printf("%s\n",(char *)val);return NULL;}int main(){pthread_t tid;int tret = pthread_create(&tid,NULL,thread,"thread run...");if( tret != 0 ){printf("create error!,info:%s\n",strerror(tret));return tret;}int ret = 0;sleep(1);if( 0 == pthread_join(tid,NULL) ){printf("pthread wait success!\n");ret = 0;}else{printf("pthread wait failed!\n");ret = 1;}return ret;}

650) this.width=650;" src="https://s4.51cto.com/wyfs02/M00/08/B2/wKiom1nlz72C1W9TAABGYidXUHI519.png" title="哈哈.png" alt="wKiom1nlz72C1W9TAABGYidXUHI519.png" />

Linux多線程總結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.