線程概念
線程(Thread)是進程(Process)?中的一個執行單元,是操作系統能夠進行運算調度的最小單位。
線程也被稱為“輕量級進程”(Lightweight Process, LWP)。
一個進程可以包含多個線程,這些線程共享該進程所擁有的全部資源(后續展開解釋)。
輕量級進程
為什么認為線程是輕量級進程,主要有兩方面:
1.線程創建:創建線程不產生新的進程地址空間,也就不需要創建對應的頁表
2.線程切換:
(1)由于進程地址空間不同,進程切換時無法從高速緩存中讀取數據,只能從較慢的內存中讀取數據,而線程由于共享地址空間,更有可能直接從緩存中讀取數據。
(2)由于進程地址空間不同,進程切換時會導致TLB失效,不能直接通過較快的TLB獲得物理地址,而是需要通過較慢的頁表來獲得物理地址,而進程由于共享地址空間,即使在緩存中讀取不到數據,也更有可能通過高速的TLB來獲得物理地址進而訪問內存
(3)相較于進程切換,線程切換時需要保存和恢復上下文更少
Linux下的線程
Linux下并沒有為線程設計獨立的概念和數據結構(即沒有與PCB相對的TCB),線程和進程都是通過task_strcut來進行管理和調度的,線程和進程也都是通過同一個底層系統調用?clone()
?來創建的。
不過為了內核對線程進行調度,線程有用于標識自身唯一性的ID:LWP(類型為pid_t)
使用命令ps -aL可以看到線程ID:
使用函數gettid()也可以獲取線程ID:
線程資源共享
1.cpu分配給進程的時間片會均分給每一個線程
2.每個線程都有和進程一樣的虛擬地址空間(這意味著理論上線程可以訪問進程所有的代碼和數據)。
3.此外,線程還共享?件描述符表 ,每種信號的處理?式,當前?作?錄 ,??id和組id。
線程獨立
線程之間共享進程的所有資源,但為了各自完成不同的任務,還需要使得線程之間在一定程度上相互獨立,這就需要每個線程持有一部分私有內容來標識自身唯一性并獨立完成任務。
因此,每個線程擁有獨立的:
線程ID,用于標識自身唯一性
信號掩碼
調度策略和優先級
錯誤碼
線程執行上下文,包括:
????????程序計數器 (Program Counter): 指示當前執行指令的位置。
????????寄存器集合 (Register Set): 存儲線程運行時的臨時數據。
線程棧 (Stack): 用于存儲函數調用時的局部變量、參數、返回地址等。每個線程的棧是私有的,這是保證線程獨立執行的關鍵。
線程局部存儲(TLS):用于存儲只能被該線程訪問的全局變量
維護線程獨立
pthread庫通過數據結構struct_pthread來維護線程獨立,struct_pthread位于共享區
而線程id就是該線程對應的struct_pthread的首地址(該線程id用于調用其他的線程API,屬于進程級,并不是內核級的、用于標識線程唯一性的線程id),類型為pthread_t
線程棧
進程在創建時會復制父進程的棧區的地址空間,在使用時可以進行寫時拷貝和動態增長?。
但由主線程?成的?線程,它的棧將不再是向下??的,?是事先在共享區占用一個固定大小的空間。
線程局部存儲
在進程內的全局變量被所有線程共享,有時我們希望一個全局變量被每個線程持有一個副本,這時就需要__thread來修飾該變量,此時該全局變量被每個線程獨立地訪問和操作
但是要注意:__thread只能用來修飾內置類型,不能用來修飾自定義類型。
編寫如下代碼進行測試:
?#include<pthread.h>
#include<unistd.h>
#include<iostream>__thread int v=100;void* task(void* arg)
{v+=(long long)arg;std::cout<<"線程"<<gettid()<<"的v是"<<v<<std::endl;return 0;
}int main()
{pthread_t t1,t2;pthread_create(&t1,NULL,task,(void*)1); pthread_create(&t2,NULL,task,(void*)2);pthread_join(t1,NULL);pthread_join(t2,NULL);
}
輸出結果:
可以看到兩個線程對全局變量的操作互不干擾
Linux線程控制
線程創建
thread:返回線程ID
attr:設置線程的屬性,attr為NULL表?使?默認屬性
start_routine:是個函數地址,線程啟動后要執?的函數
arg:傳給線程啟動函數的參數
返回值:成功返回0,失敗返回錯誤碼
線程終止
終止當前線程:
retval:用于輸出線程要執行的函數的返回值
終止指定線程:
返回值:成功返回0,失敗返回錯誤碼
終止某一線程的三種方法:
1.從線程函數return。這種?法對主線程不適?,從main函數return相當于調?exit。
2. 線程可以調?pthread_ exit終???。
3. ?個線程可以調?pthread_ cancel終?同?進程中的另?個線程。
線程等待
主線程等待指定線程結束
thread:用于指定線程
retval:用于傳出線程執行函數的返回值
線程分離
線程有兩種狀態:joinable和detached
默認情況下,新創建的線程是joinable的,線程退出后,需要主線程調用pthread_join,否則?法釋放資源,從?造成系統泄漏。
如果不關?線程的返回值,可以調用pthread_detach,當線程退出時,?動釋放線程資源。
適用于主線程為死循環的情景
thread:用于指定線程
注意:pthread_join和pthread_exit傳參時不要傳局部變量給retval,因為線程返回的值必須在線程結束后仍然有效。
多線程優缺點
優點:
1.創建與切換開銷低
2.在等待慢速I/O操作結束的同時,程序可執行其他的計算任務
3.對于計算密集型應用,為了能在多處理系統上運行,將計算分解到多個線程中實現
4.對于I/O 密集型應用,為了提高性能,將I/O操作重疊。線程可以同時等待不同的I/O操作。
缺點:
1.性能損失
一個很少被外部事件阻塞的計算密集型線程往往無法與其他線程共享同一個處理器。如果計算密集型線程的數量比可用的處理器多,那么可能會有額外的同步和調度開銷。
2.健壯性降低
線程共享數據容易引發線程安全問題。
單個線程如果出現除零,野指針問題導致線程崩潰,進程也會隨之崩潰。
3.缺乏訪問控制
進程是訪問控制的基本粒度,在一個線程中調用某些OS函數會對整個進程造成影響。
4.編程難度提高
編寫與調試一個多線程程序比單線程程序困難得多??