目錄
一、線程間通信基礎
1. 概念
2. 通信基礎:共享空間
二、互斥鎖(Mutex)
1. 概念
2. 使用流程
3. 函數接口
三、死鎖
1. 概念
2. 死鎖產生的 4 個必要條件
3. 避免死鎖的方法
四、信號量(Semaphore)
1. 概念
2. 函數接口
3. 應用場景
一、線程間通信基礎
1. 概念
線程間通信指多個線程之間傳遞信息的過程,是多線程協作完成任務的核心機制
2. 通信基礎:共享空間
同一進程中的多個線程共享以下資源,這是線程間通信的基礎:
- 文本段(代碼區)
- 數據段(全局變量、靜態變量等)
- 堆區(動態分配的內存空間)
注意:線程獨享棧區(默認 8M),棧區數據不共享;因此線程間通信主要依賴共享的全局變量、堆區數據等
采用全局變量的原因:
- 進程是操作系統資源分配的最小單元
- 每個進程空間獨立的,包含文本段+數據段(全局變量)+系統數據段
- 一個進程中的多個線程獨享棧空間,文本段、數據段、堆區進程多線程共享
同一進程的線程共享全局變量、靜態變量、堆區數據(malloc 分配),但局部變量(棧區)不共享;因此線程間通信需通過共享變量,避免使用棧區數據
- 多線程同時操作共享空間會引發資源競爭,需要加上互斥鎖解決資源競爭問題
二、互斥鎖(Mutex)
1. 概念
- 互斥鎖是解決多線程資源競爭的核心機制,可視為一種 "獨占性資源"
- 特性:同一時間只能有一個線程獲得鎖,加鎖期間其他線程需等待解鎖后才能再次加鎖
- 臨界區:加鎖和解鎖之間的代碼段,即被互斥鎖保護的共享資源操作代碼
- 只能防止多個線程對資源的競爭,不能決定代碼的先后執行順序
- 原子操作:CPU 執行加鎖 / 解鎖操作時無法切換任務,保證操作的不可分割性
2. 使用流程
3. 函數接口
函數名 | 原型 | 功能 | 參數說明 | 返回值 |
---|---|---|---|---|
pthread_mutex_init | int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr) | 初始化互斥鎖 | -? 互斥鎖變量地址 鎖屬性(默認 NULL,使用默認屬性) | 成功返回 0 失敗返回 - 1 |
pthread_mutex_lock | int pthread_mutex_lock(pthread_mutex_t *mutex) | 互斥鎖加鎖 |
互斥鎖變量地址 | 成功返回 0 失敗返回 - 1(若鎖已被占用,會阻塞等待) |
pthread_mutex_unlock | int pthread_mutex_unlock(pthread_mutex_t *mutex) | 互斥鎖解鎖 |
互斥鎖變量地址 | 成功返回 0 失敗返回 - 1 |
pthread_mutex_destroy | int pthread_mutex_destroy(pthread_mutex_t *mutex) | 銷毀互斥鎖 |
互斥鎖變量地址 | 成功返回 0 失敗返回 - 1 |
三、死鎖
1. 概念
多線程因加鎖 / 解鎖順序錯誤,導致線程相互等待對方釋放鎖資源,程序無法繼續執行的狀態。
2. 死鎖產生的 4 個必要條件
條件 | 說明 |
---|---|
互斥條件 | 資源(如鎖)只能被一個線程占用 |
不可剝奪條件 | 線程占用的資源不能被強制剝奪 |
請求保持條件 | 線程持有部分資源,同時請求其他資源 |
循環等待條件 | 線程間形成資源請求循環(如線程 1 等待線程 2 的鎖,線程 2 等待線程 1 的鎖) |
3. 避免死鎖的方法
- 保持加鎖順序一致:所有線程按固定順序加鎖(如先鎖 A 再鎖 B)
- 使用非阻塞加鎖:用
pthread_mutex_trylock
(嘗試加鎖,失敗時不阻塞,返回錯誤碼)替代pthread_mutex_lock
- 限時加鎖:設置加鎖超時時間,超時后釋放已持有的鎖
四、信號量(Semaphore)
1. 概念
- 信號量是一種資源
- 信號量只能完成四種操作:初始化、銷毀、申請、釋放
- 如果信號量資源數為0,申請資源會阻塞等待,直到占用資源的任務釋放資源,資源數不為0時才能申請到資源并繼續向下執行
- 釋放資源不會阻塞
2. 函數接口
函數名 | 原型 | 功能 | 參數說明 | 返回值 |
---|---|---|---|---|
sem_init | int sem_init(sem_t *sem, int pshared, unsigned int value) | 初始化信號量 | -? 信號量變量地址 0 表示線程間共享,非 0 表示進程間共享 初始資源數量 | 成功返回 0 失敗返回 - 1 |
sem_destroy | int sem_destroy(sem_t *sem) | 銷毀信號量 |
信號量變量地址 | 成功返回 0 失敗返回 - 1 |
sem_wait | int sem_wait(sem_t *sem) | 申請信號量(P 操作) |
信號量變量地址 | 成功返回 0(資源數 - 1) 失敗返回 - 1;資源數為 0 時阻塞 |
sem_post | int sem_post(sem_t *sem) | 釋放信號量(V 操作) |
信號量變量地址 | 成功返回 0(資源數 + 1) 失敗返回 - 1 |
注意:
- 申請信號量會讓信號量資源數-1
- 如果信號量資源數為0,則會阻塞等待,直到有任務釋放資源,才能拿到資源并繼續向下 執行
3. 應用場景
- 控制并發訪問數量(如限制同時訪問共享內存的線程數)
- 實現線程間的同步(如生產者 - 消費者模型中控制數據讀寫順序)
注意:
- 互斥鎖與信號量的區別
- 互斥鎖:用于 "獨占" 資源(資源數固定為 1),解決資源競爭
- 信號量:用于 "計數" 資源(資源數可自定義),控制并發數量或同步
? ? ?2.臨界區設計原則
- 臨界區代碼應盡可能精簡,僅包含對共享資源的操作,減少線程等待時間,提高效率