Linux多線程(一)
一、線程的概念
1、基本了解程序和線程
如果把計算機的運作過程比作為一個生産企業,假設它目前隻有一個廠區而且這個廠區中隻有一條生産流水線。随着企業的不斷發展,企業就需要擴大自己的規模,那麼有以下兩種方法:
- 開一個新的廠區,成本比較高 ==>> 建立了一個新的程序
- 在原有的廠區中增加流水線,成本低 ==>> 建立了一個新的線程
這個“成本”最直接的展現就是:
- 每次建立一個新的程序,會配置設定一個新的虛拟位址空間;
- 每次建立一個新的線程,線程會共用原來的虛拟位址空間;
那麼同樣的情況下,同一個廠區可以包含更多的流水線,是以我們可以了解為:線程是運作在程序之中的(一個程序包含了若幹的線程);
2、線程和程序的差別
程序:資源的管理(管理記憶體、管理打開的檔案)
線程:排程和執行(和程序類似,也是搶占式的排程)
在Linux作業系統中把線程叫做 輕量級程序(LWP)
3、線程優點/缺點(相比于程序)
優點:建立/銷毀開銷更小、切換排程的開銷更小、線程占用的資源更小,其本質上就是線程公用同一個虛拟位址空間;
缺點:
健壯性降低:一個線程異常終止會導緻程序異常終止;
程式設計/調試難度變大:編寫與調試一個多線程程式比單線程程式困難得多;
——>對線程的可靠性要求更高;
——>線程安全問題;
4、應用場景(多線程/多程序)
CPU密集型:CPU計算量很大(while(1);)
IO密集型:通過網絡進行輸入輸出、響應UI界面(界面顯示和資料計算要多線程完成,防止由于資料計算太久導緻界面卡死);
5、線程之間的資源
公用的資源:虛拟位址空間、檔案描述符表
不公用的資源:棧(函數調用棧,局部變量等)每一個線程都一個自己獨立的棧、上下文資訊(CPU中的寄存器資訊)、每個線程有自己的單獨的errno錯誤碼(thread local);
- 線程并不是越多越好,達到一定的數目後效率就沒辦法提升;
- 線程如果多了,多個線程嘗試通路同一個資源,就會打架(互斥);
- 某個線程可能一直得不到執行的機會,線程饑餓(同步);
- 如果某個線程異常終止,整個程序都會異常終止;
二、線程的實作
線程控制的相關函數:
線程控制相關函數不是系統調用!庫函數 => posix 線程庫
線程庫函數的幾個簡單的使用
#include<pthread.h>
//POSIX線程庫
//連接配接這些線程函數時要使用編輯器指令的'-lpthraed'選項
//對于類型 pthread_t
#include<bits/pthreadtypes.h>
typedef unsigned long int pthread_t;
1、建立線程
pthread_create(pthread_t *thread,const pthread_attr_t *attr,
void* (*start_routine)(void*),void *arg);
//pthread_t *thread:輸出型參數,傳回線程的ID
//pthread_attr_t *attr:設定線程結構的結構體,通常置NULL
//void* (*start_routine)(void*):是個函數位址,線程啟動後要執行的函數
//void *arg:傳給線程啟動函數的參數
//return:成功傳回0,失敗傳回errno
2、終止線程(三種方法)
- 從線程函數return,這種方法對主線程不适用,從main函數return相當于調用了exit();
- 線程可以調用pthread_exit終止自己;
- 一個線程可以調用pthread_cancel終止同一個程序中的另一個線程;
//擷取線程自身的ID
pthread_t pthread_self(void);
//pthread_t:本質上就是一個程序位址空間上的一個位址;
//renturn:傳回目前線程的ID
//終止所有線程
void pthread_exit(void* value_ptr);
//value_ptr:
//return:無傳回值,線程結束時無法傳回到它的調用者
//終止同一個程序中的另一個程序
int pthread_cancel(pthread_t thread);
//thread:線程ID
//renturn:成功傳回0,失敗傳回錯誤碼
3、等待線程
等待線程的意義:已經退出的線程,其空間并沒有被釋放,仍然在程序的位址空間中,建立新的程序可能會複用剛才退出的線程的位址空間;
int pthread_join(pthread_t thread,void **value_ptr);
//thread:線程ID
//value_ptr:它指向一個指針,後者指向線程的傳回值
//return:成功傳回0;失敗傳回錯誤碼
調用了這函數的線程将會被挂起等待,直到ID為thread的線程被終止。thread以不同的方式被終止,通過pthread_join得到的終止狀态是不同的:
- 如果thread線程通過return傳回,value_ptr所指向的單元裡存放的是thread線程函數的傳回值;
- 如果thread線程被别的線程調用pthread_cancel異常終止,value_ptr裡面存放的是常數PTHREAD_CANCELED;
- 如果thread線程是自己調用pthread_exit終止的,value_ptr所指向的單元裡面存放的是傳給pthread_exit的參數;
- 如果對thread線程的終止狀态不太感興趣,可以傳NULL給value_ptr;
4、線程分離
int pthread_detach(pthread_t thread);
//pthread_t thread:線程ID
//return:成功傳回0,失敗傳回errno
- 預設情況下,新建立的線程是joinable的,線程退出後,需要對其進行pthread_join操作,否則無法釋放資源,進而造成系統洩漏。
- 如果不關心線程的傳回值,join是一種負擔,這個時候,我們可以告訴系統,當線程退出時,自動釋放線程資源。