目錄
線程的優點
pthread 線程庫?
前言
認識線程庫
簡單驗證線程的獨立棧空間
線程的優點
與進程之間的切換相比,線程之間的切換需要操作系統做的工作要少得多。
調度進程時,CPU 中有一個 cache(緩存,提高運行效率),CPU在虛擬地址轉物理地址后,在內存中找到了一行代碼,然而程序在運行時,比如運行到了第50行代碼,下一次大概率會運行下一行代碼,也就是第51行代碼,也有可能跳轉到其他代碼,但是大概率還是運行下一行代碼,所以系統把第50行代碼的周邊代碼都加載到 cache 中,CPU 在運行時直接從 cache 中讀取代碼,提高 CPU 尋址效率。
進程切換時,會把當前緩存到 cache 的數據都切換掉,而線程切換時,寄存器中的數據會被切換,但 cache 中的數據不會被丟棄,大概率還是可以用的,線程切換只需要更改少量寄存器和棧指針等信息,而不需要像進程那樣進行完整的上下文切換。所以線程切換時操作系統做的工作比進程的切換少。
創建一個新線程的代價要比創建一個新進程小得多,線程占用的資源比進程少得多。
進程是資源分配的基本單位,線程是調度的基本單位,當多個線程屬于同一個進程時,它們會共享以下資源:
地址空間:包括代碼段、數據段、堆區和棧區(每個線程有自己的棧)。所有線程都可以訪問進程的整個虛擬地址空間,這意味著它們可以讀寫相同的全局變量和靜態變量。
打開的文件描述符:如文件、網絡連接等。所有線程都能操作這些描述符,因此對文件或網絡連接的操作可以在不同線程間共享。
環境變量:進程啟動時設置的環境變量是所有線程共有的。
內存映射:如果進程使用了內存映射文件或其他形式的內存映射,那么這些映射也是所有線程可見并可訪問的。
信號處理程序:雖然信號通常是針對整個進程的,但某些信號(如SIGSEGV)可以由特定線程捕捉到,并且線程可以安裝自己的信號處理器。
當前工作目錄:所有線程共享同一進程的工作目錄,任何線程改變工作目錄都會影響其他線程。
用戶ID和組ID:與安全性和權限相關的標識信息是所有線程共有的。
資源限制:例如最大文件大小、CPU時間等,這些限制適用于整個進程,因此也適用于所有線程。
因為多個線程共享資源,所以一個線程占用的資源比進程少。
但是線程也會有自己私有的資源:
棧空間:每個線程都有自己的棧,用于存儲函數調用時的局部變量、返回地址等信息。這是線程之間保持獨立性的關鍵之一,因為每個線程可以在其棧上進行獨立的操作而不干擾其他線程。
寄存器集合(上下文數據):當線程被調度執行時,它有自己的寄存器集,包括程序計數器(PC)、堆棧指針和其他通用寄存器。這些寄存器保存了線程執行的狀態信息。
線程ID:操作系統賦予每個線程一個唯一的標識符(TID),用于區分不同的線程。這個ID是線程私有的,因為它唯一地識別了一個線程。
線程優先級和調度屬性:某些系統允許為每個線程設定獨立的調度參數,如優先級和策略,這些屬性影響線程的執行順序和時間片分配。?
pthread 線程庫?
前言
Linux 的線程是用進程模擬的,線程在Linux底層被視為輕量級進程。對于同一個進程中的新、主線程,可以看出線程的 tid 和 LWP 的數值是不一樣的:
線程被創建、等待、分離、終止,且擁有獨立的棧結構,這些都是系統在管理線程,
1、系統管理線程時,并沒有對用戶暴露 在系統中線程被視為輕量級進程的事實,用戶認為那就是線程;
2、系統中沒有線程的概念,只有輕量級進程的概念,用戶卻能創建管理、操作線程。
能實現以上兩點是因為系統對底層的輕量級進程進行了封裝,用戶能操作線程都是因為有了庫,所以在Linux中線程也叫做用戶級線程。既然線程因庫而起,就應該由庫來維護。
認識線程庫
為了管理線程,“先描述再組織”,定義線程的控制塊 TCB,TCB是操作系統內核用來管理和調度線程的數據結構。
#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
pthread_create 的第一個參數 thread 是一個 pthread_t 類型的輸出型參數,函數調用結束后,thread 指向一個虛擬內存單元,該內存單元的地址即為新創建線程的線程 ID,所以 pthread_t 類型的線程 ID 實際上就是一個進程地址空間的一個地址!線程庫的后續操作就是根據該線程 ID 來操作線程的,用戶也可以調用 pthread_self 函數來獲得線程自身的 ID。
線程庫中還包含了一系列用于創建、管理和操作線程的函數、類和數據結構。具體來說,一個典型的線程庫會提供以下組件:
1. 線程管理
創建線程:函數或構造函數用來啟動一個新的線程,通常需要指定要在線程中執行的函數(即線程函數)。
- 示例:
pthread_create()
?(POSIX Threads)銷毀/終止線程:方法來結束線程的執行,可以是自然結束(當線程函數返回時),也可以是通過特定API強制結束。
- 示例:
pthread_cancel()
,?std::thread::join()
?或?std::thread::detach()
等待線程完成:允許主線程或其他線程等待某個特定線程完成其任務。
- 示例:
pthread_join()
,?std::thread::join()
2. 線程同步機制
為了確保多個線程之間安全地共享資源,線程庫提供了各種同步工具:
互斥鎖(Mutex):防止多個線程同時訪問臨界區代碼段。
- 示例:
pthread_mutex_t
,?std::mutex
讀寫鎖(Read-write Locks):允許多個讀者或單個寫者訪問資源。
- 示例:
pthread_rwlock_t
條件變量(Condition Variables):用于線程間的通信,一個線程可以在滿足特定條件時喚醒另一個線程。
- 示例:
pthread_cond_t
,?std::condition_variable
信號量(Semaphores):控制對有限數量資源的訪問。
- 示例:
sem_t
?(POSIX Semaphores)
3. 線程屬性設置
- 設置線程屬性:在創建線程之前,可以設定一些線程屬性,如棧大小、調度策略等。
- 示例:
pthread_attr_t
?(POSIX Threads)
4. 線程本地存儲(TLS)
- 線程局部數據:為每個線程提供獨立的數據副本,即使這些變量是在全局范圍內聲明的。
- 示例:
pthread_key_create()
,?pthread_getspecific()
,?pthread_setspecific()
,?std::thread_local
?(C++)
5. 高級特性
線程池:預先創建一組工作線程,以便快速響應任務請求而不必頻繁創建和銷毀線程。
- 示例:?C++ 中可以通過第三方庫如Boost實現。
并發容器:線程安全的數據結構,例如隊列、堆棧、哈希表等。
- 示例:
std::shared_timed_mutex
,?concurrent_queue
?(Intel TBB)原子操作:提供無鎖編程的支持,保證某些操作的原子性。
- 示例:
std::atomic
?(C++)
6. 工具和輔助函數
當前線程信息:獲取當前線程ID等信息。
- 示例:
pthread_self()
,?std::this_thread::get_id()
線程調度:調整線程優先級或讓出CPU給其他線程。
- 示例:
sched_yield()
,?std::this_thread::yield()
簡單驗證線程的獨立棧空間
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>void* newthreadRun(void* args)
{std:string threadname=(char*)args;int cnt=5;while(true){std::cout<<"I am "<<threadname<<",cnt: "<<cnt<<", &cnt: "<<&cnt<<std::endl;cnt--;sleep(1);}return nullptr;
}int main()
{pthread_t tid1;pthread_t tid2;pthread_create(&tid1,nullptr,newthreadRun,(void*)"thread-1"); pthread_create(&tid2,nullptr,newthreadRun,(void*)"thread-2");pthread_join(tid2,nullptr);pthread_join(tid1,nullptr);return 0;
}
同一局部變量的地址不同, 說明每個線程的棧空間都私有一份該變量:
?