天天看點

【Linux】線程概念及建立、終止、等待和分離Linux多線程(一)

Linux多線程(一)

一、線程的概念

1、基本了解程序和線程

如果把計算機的運作過程比作為一個生産企業,假設它目前隻有一個廠區而且這個廠區中隻有一條生産流水線。随着企業的不斷發展,企業就需要擴大自己的規模,那麼有以下兩種方法:

  • 開一個新的廠區,成本比較高 ==>> 建立了一個新的程序
  • 在原有的廠區中增加流水線,成本低 ==>> 建立了一個新的線程

這個“成本”最直接的展現就是:

  • 每次建立一個新的程序,會配置設定一個新的虛拟位址空間;
  • 每次建立一個新的線程,線程會共用原來的虛拟位址空間;

那麼同樣的情況下,同一個廠區可以包含更多的流水線,是以我們可以了解為:線程是運作在程序之中的(一個程序包含了若幹的線程);

2、線程和程序的差別

程序:資源的管理(管理記憶體、管理打開的檔案)

線程:排程和執行(和程序類似,也是搶占式的排程)

在Linux作業系統中把線程叫做 輕量級程序(LWP)

3、線程優點/缺點(相比于程序)

優點:建立/銷毀開銷更小、切換排程的開銷更小、線程占用的資源更小,其本質上就是線程公用同一個虛拟位址空間;

缺點:

健壯性降低:一個線程異常終止會導緻程序異常終止;

程式設計/調試難度變大:編寫與調試一個多線程程式比單線程程式困難得多;

——>對線程的可靠性要求更高;

——>線程安全問題;

4、應用場景(多線程/多程序)

CPU密集型:CPU計算量很大(while(1);)

IO密集型:通過網絡進行輸入輸出、響應UI界面(界面顯示和資料計算要多線程完成,防止由于資料計算太久導緻界面卡死);

5、線程之間的資源

公用的資源:虛拟位址空間、檔案描述符表

不公用的資源:棧(函數調用棧,局部變量等)每一個線程都一個自己獨立的棧、上下文資訊(CPU中的寄存器資訊)、每個線程有自己的單獨的errno錯誤碼(thread local);

  1. 線程并不是越多越好,達到一定的數目後效率就沒辦法提升;
  2. 線程如果多了,多個線程嘗試通路同一個資源,就會打架(互斥);
  3. 某個線程可能一直得不到執行的機會,線程饑餓(同步);
  4. 如果某個線程異常終止,整個程序都會異常終止;

二、線程的實作

線程控制的相關函數:

線程控制相關函數不是系統調用!庫函數 => 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、終止線程(三種方法)

  1. 從線程函數return,這種方法對主線程不适用,從main函數return相當于調用了exit();
  2. 線程可以調用pthread_exit終止自己;
  3. 一個線程可以調用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是一種負擔,這個時候,我們可以告訴系統,當線程退出時,自動釋放線程資源。

繼續閱讀