C++中鎖的種類與使用實例詳解
關(guān)于c++中鎖的種類與使用
Optimized tool selectionC++ 標(biāo)準(zhǔn)庫中有多種鎖管理機制,各有不同的特性和適用場景:
看這段代碼中使用的是 std::lock_guard,這是一個自動管理鎖的工具。鎖會在以下時刻釋放:
鎖的生命周期
.....
static std::mutex g_mutex; // 保護 g_control / g_reply 的并發(fā)訪問
.....
void handleControlGram(const ControlGram &cg) {
std::lock_guard<std::mutex> lk(g_mutex); // ← 獲取鎖
g_control.SteeringThrottle = (uint8_t)cg.SteeringThrottle;
// ... 其他賦值操作
g_control.ControlMode = cg.ControlMode;
} // ← 函數(shù)結(jié)束時,lk 對象析構(gòu),鎖自動釋放
鎖的釋放時機:
- 函數(shù)返回時 —
lock_guard對象超出作用域,析構(gòu)函數(shù)被調(diào)用,鎖自動釋放 - 無需手動調(diào)用
unlock()
為什么使用 lock_guard?
這是 RAII 模式(Resource Acquisition Is Initialization)的應(yīng)用:
| 情形 | 手動 lock/unlock | lock_guard |
|---|---|---|
| 正常執(zhí)行 | ? 釋放 | ? 自動釋放 |
| 異常拋出 | ? 鎖泄露 | ? 析構(gòu)仍會釋放 |
| 邏輯分支 | ? 容易遺漏 | ? 自動保證 |
在 C++ 中,鎖是解決多線程數(shù)據(jù)競爭的核心工具(C++11 及以后標(biāo)準(zhǔn)提供了完善的鎖機制)。針對你的 NVIDIA Orin(Ubuntu20.04,ARM 架構(gòu)),所有標(biāo)準(zhǔn) C++ 鎖都可以直接使用,編譯時僅需鏈接線程庫(-pthread)。
我會分 底層互斥體(鎖本體)、RAII 自動鎖管理工具(推薦必用) 兩部分講解,包含種類、特性、區(qū)別、使用場景、代碼示例,最后給避坑指南。
一、前置核心知識
- 作用:保護共享資源(變量、文件、硬件接口),保證同一時間只有一個/指定線程訪問,避免數(shù)據(jù)錯亂。
- 核心頭文件:
#include <mutex> // C++11 基礎(chǔ)鎖 #include <shared_mutex> // C++17 讀寫鎖 #include <thread> // 線程
- 黃金原則:永遠(yuǎn)用 RAII 自動管理鎖,不要手動
lock()/unlock()(極易忘記解鎖導(dǎo)致死鎖)。 - 編譯命令(Orin 通用):
g++ -std=c++17 代碼.cpp -o 程序 -pthread
二、第一部分:C++ 標(biāo)準(zhǔn)底層互斥體(鎖的本體)
這是鎖的核心實現(xiàn),分為 獨占鎖、遞歸鎖、超時鎖、讀寫鎖 4 大類,共 5 種:
| 互斥體類型 | 獨占性 | 遞歸 | 超時 | 共享讀寫 | C++ 版本 | 核心特性 |
|---|---|---|---|---|---|---|
std::mutex | ? 獨占 | ? | ? | ? | C++11 | 最基礎(chǔ)、性能最高 |
std::recursive_mutex | ? 獨占 | ? | ? | ? | C++11 | 同一線程可重復(fù)加鎖 |
std::timed_mutex | ? 獨占 | ? | ? | ? | C++11 | 可嘗試加鎖,超時放棄 |
std::recursive_timed_mutex | ? 獨占 | ? | ? | ? | C++11 | 遞歸+超時 |
std::shared_mutex | ? 獨占 | ? | ? | ? 讀寫分離 | C++17 | 讀共享、寫?yīng)氄?/td> |
1.std::mutex(基礎(chǔ)獨占鎖)
- 特性:獨占排他鎖,同一時間只能被一個線程持有;不可遞歸、不可超時、不可共享。
- 適用場景:90% 的常規(guī)多線程場景(簡單共享變量保護)。
- 禁止操作:同一個線程不能加鎖兩次(直接死鎖)。
2.std::recursive_mutex(遞歸獨占鎖)
- 特性:允許同一個線程多次加鎖,必須解鎖相同次數(shù)才會釋放;其他特性同
mutex。 - 適用場景:遞歸函數(shù)、嵌套調(diào)用中需要加鎖的場景(極少用,能不用就不用)。
3.std::timed_mutex(超時獨占鎖)
- 特性:支持超時嘗試加鎖,不會永久阻塞線程。
- API:
try_lock_for(時間)、try_lock_until(時間點)。 - 適用場景:需要避免永久死等鎖的場景(如硬件通信、超時任務(wù))。
4.std::shared_mutex(讀寫鎖 / 共享互斥鎖,C++17)
- 核心特性:讀寫分離
- 讀模式(共享):多個線程可以同時加鎖讀數(shù)據(jù);
- 寫模式(獨占):只有一個線程能加鎖寫數(shù)據(jù),寫時禁止讀。
- 適用場景:讀多寫少(如配置讀取、日志、緩存),性能遠(yuǎn)高于普通獨占鎖。
三、第二部分:RAII自動鎖管理工具(必用!)
直接操作底層互斥體容易忘記解鎖,C++ 提供了 4 種自動鎖工具,構(gòu)造時加鎖,析構(gòu)時自動解鎖:
| 管理工具 | 配合鎖類型 | 手動解鎖 | 超時支持 | 多鎖同時加鎖 | 核心用途 |
|---|---|---|---|---|---|
std::lock_guard | 所有獨占鎖 | ? | ? | ? | 最簡單、最常用(默認(rèn)首選) |
std::unique_lock | 所有獨占鎖 | ? | ? | ? | 靈活鎖(配合條件變量、手動控制) |
std::shared_lock | shared_mutex | ? | ? | ? | 讀寫鎖的讀模式(共享) |
std::scoped_lock | 所有鎖 | ? | ? | ? | C++17 同時鎖多個鎖,防死鎖 |
四、完整使用示例(可直接在 Orin 上運行)
示例 1:基礎(chǔ)用法std::mutex + lock_guard(首選)
最常用,保護簡單共享變量:
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
mutex mtx; // 全局互斥鎖
int share_num = 0; // 共享資源
void add_num() {
// 構(gòu)造自動加鎖,離開作用域自動解鎖
lock_guard<mutex> lock(mtx);
share_num++;
cout << "線程ID: " << this_thread::get_id() << " 數(shù)值: " << share_num << endl;
}
int main() {
thread t1(add_num);
thread t2(add_num);
t1.join();
t2.join();
return 0;
}示例 2:遞歸鎖recursive_mutex
解決遞歸函數(shù)加鎖問題:
#include <mutex>
recursive_mutex rmtx;
void func(int n) {
lock_guard<recursive_mutex> lock(rmtx);
if (n > 0) func(n-1); // 同一線程重復(fù)加鎖,無死鎖
}示例 3:超時鎖timed_mutex + unique_lock
避免永久阻塞:
#include <mutex>
#include <chrono>
timed_mutex tmtx;
void try_lock_func() {
// 嘗試加鎖,最多等待 100ms
unique_lock<timed_mutex> lock(tmtx, chrono::milliseconds(100));
if (lock.owns_lock()) {
cout << "加鎖成功" << endl;
} else {
cout << "加鎖超時" << endl;
}
}示例 4:讀寫鎖shared_mutex(讀多寫少最優(yōu))
#include <shared_mutex>
shared_mutex s_mtx;
int data = 0;
// 讀線程:共享加鎖,多個線程可同時讀
void read_data() {
shared_lock<shared_mutex> lock(s_mtx);
cout << "讀取數(shù)據(jù): " << data << endl;
}
// 寫線程:獨占加鎖,同一時間只能一個寫
void write_data(int val) {
unique_lock<shared_mutex> lock(s_mtx);
data = val;
cout << "寫入數(shù)據(jù): " << data << endl;
}示例 5:多鎖防死鎖scoped_lock(C++17)
同時加多個鎖,避免死鎖:
mutex m1, m2;
void safe_lock() {
// 一次性加鎖 m1 和 m2,自動避免死鎖
scoped_lock lock(m1, m2);
}
五、關(guān)鍵區(qū)別與選型指南(核心總結(jié))
1. 獨占鎖 vs 讀寫鎖
- 獨占鎖(mutex):讀寫都互斥,適合讀寫頻率差不多的場景;
- 讀寫鎖(shared_mutex):讀不互斥、寫互斥,適合讀多寫少(性能提升 5~10 倍)。
2. lock_guard vs unique_lock
- lock_guard:輕量、無額外開銷,日常開發(fā)首選;
- unique_lock:靈活(可手動解鎖、超時、配合條件變量),有微小開銷。
3. 普通鎖 vs 遞歸鎖
- 99% 的場景不要用遞歸鎖,它會掩蓋代碼設(shè)計問題,僅用于遞歸函數(shù)。
六、避坑指南(Orin 開發(fā)必看)
- 絕對不要手動調(diào)用
lock()/unlock()
異常、提前 return 都會導(dǎo)致鎖無法釋放,直接死鎖。 - 鎖粒度盡可能小
只保護共享資源,不要在鎖內(nèi)執(zhí)行耗時操作(如網(wǎng)絡(luò)、打印)。 - 避免死鎖
- 多鎖必須用
scoped_lock同時加鎖; - 不要循環(huán)等待鎖。
- 多鎖必須用
- ARM 平臺(Orin)優(yōu)化建議
短臨界區(qū)(幾行代碼)優(yōu)先用 自旋鎖(std::spin_lockC++20),避免線程切換開銷。
七、快速選型口訣
- 常規(guī)場景 →
mutex + lock_guard - 讀多寫少 →
shared_mutex + shared_lock - 需要靈活控制/條件變量 →
unique_lock - 多鎖防死鎖 →
scoped_lock - 遞歸調(diào)用 →
recursive_mutex - 不想永久等待 →
timed_mutex
總結(jié)
- C++ 鎖分底層互斥體(5種)和 RAII 管理工具(4種),永遠(yuǎn)用 RAII 自動管理;
- 基礎(chǔ)開發(fā)用
std::mutex + std::lock_guard足夠; - 讀多寫少用讀寫鎖,性能最優(yōu);
- Orin(ARM)編譯加
-std=c++17 -pthread,代碼全平臺通用。
到此這篇關(guān)于關(guān)于c++中鎖的種類與使用的文章就介紹到這了,更多相關(guān)c++鎖的種類與使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
編譯錯誤error: stray ‘\343’in program的解決方法
以下是對編譯錯誤error: stray ‘\343’in program的解決方法進(jìn)行了詳細(xì)的分析介紹,如遇此問題的朋友們可以過來參考下2013-07-07
vscode使用cmake時將命令行參數(shù)傳遞給調(diào)試目標(biāo)的方法
這篇文章主要介紹了vscode使用cmake時將命令行參數(shù)傳遞給調(diào)試目標(biāo),下面介紹了一個示例,將參數(shù)first_arg, second-arg和third arg傳遞給程序(此處需要注意,third arg中間雖然存在空格,但是仍然被視作一個參數(shù)),需要的朋友參考下吧2024-03-03
OpenCV中的cv::Mat函數(shù)將數(shù)據(jù)寫入txt文件
這篇文章主要介紹了OpenCVcv::Mat中的數(shù)據(jù)按行列寫入txt文件中,需要的朋友可以參考下2018-05-05
C語言中怎么在main函數(shù)開始前執(zhí)行函數(shù)
C語言中怎么在main函數(shù)開始前執(zhí)行函數(shù)呢?下面小編就大家詳細(xì)的介紹一下。需要的朋友可以過來參考下,希望對大家有所幫助2013-10-10
linux安裝mysql和使用c語言操作數(shù)據(jù)庫的方法 c語言連接mysql
Linux下使用C語言操作數(shù)據(jù)庫的方法,我將從MySQL環(huán)境的搭建,MySQL命令的使用到使用C接口來操作MySQL等過程詳細(xì)的介紹在Linux下管理MySQL數(shù)據(jù)庫的方法2014-01-01

