C++多線程與鎖機(jī)制使用解讀
1. 基本多線程編程
1.1 創(chuàng)建線程
#include <iostream>
#include <thread>
void thread_function() {
std::cout << "Hello from thread!\n";
}
int main() {
std::thread t(thread_function); // 創(chuàng)建并啟動(dòng)線程
t.join(); // 等待線程結(jié)束
return 0;
}1.2 帶參數(shù)的線程函數(shù)
#include <thread>
#include <iostream>
void print_num(int num) {
std::cout << "Number: " << num << "\n";
}
int main() {
std::thread t(print_num, 42);
t.join();
return 0;
}1.3 join() 和 detach()
std::thread t(threadFunction);
// join() - 等待線程完成
t.join();
// detach() - 分離線程,線程獨(dú)立運(yùn)行
// t.detach();
// 檢查線程是否可joinable
if (t.joinable()) {
t.join();
}1.4 獲取當(dāng)前線程信息
#include <thread>
#include <iostream>
int main() {
std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
std::thread t([](){
std::cout << "Worker thread ID: " << std::this_thread::get_id() << std::endl;
});
t.join();
return 0;
}1.5 線程休眠
#include <chrono>
#include <thread>
int main() {
std::cout << "Sleeping for 2 seconds..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Awake!" << std::endl;
return 0;
}2. mutex (互斥鎖)
#include <thread>
#include <mutex>
#include <iostream>
std::mutex mtx; // 全局互斥鎖
int shared_data = 0;
void increment() {
for (int i = 0; i < 100000; ++i) {
mtx.lock(); // 上鎖
++shared_data;
mtx.unlock(); // 解鎖
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final value: " << shared_data << "\n";
return 0;
}2.1 lock_guard (自動(dòng)管理鎖)
lock_guard 在構(gòu)造時(shí)自動(dòng)上鎖,在析構(gòu)時(shí)自動(dòng)解鎖,防止忘記解鎖
void increment_safe() {
for (int i = 0; i < 100000; ++i) {
std::lock_guard<std::mutex> lock(mtx); // 自動(dòng)上鎖
++shared_data;
} // 自動(dòng)解鎖
}2.2 unique_lock
unique_lock 比 lock_guard 更靈活,可以手動(dòng)上鎖和解鎖。
void increment_flexible() {
for (int i = 0; i < 100000; ++i) {
std::unique_lock<std::mutex> lock(mtx);
++shared_data;
lock.unlock(); // 可以手動(dòng)解鎖
// 做一些不需要鎖的操作
lock.lock(); // 再手動(dòng)上鎖
++shared_data;
}
}2.3 嘗試鎖try_lock()
void tryLockExample() {
std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);
if (lock.owns_lock()) {
// 成功獲取鎖
std::cout << "Got the lock!\n";
} else {
// 未能獲取鎖
std::cout << "Couldn't get the lock, doing something else...\n";
}
}2.4 遞歸互斥鎖std::recursive_mutex
#include <mutex>
std::recursive_mutex rec_mtx;
void recursiveFunction(int count) {
std::lock_guard<std::recursive_mutex> lock(rec_mtx);
if (count > 0) {
std::cout << "Count: " << count << '\n';
recursiveFunction(count - 1);
}
}
int main() {
std::thread t(recursiveFunction, 3);
t.join();
return 0;
}2.5 定時(shí)互斥鎖std::timed_mutex
#include <mutex>
#include <chrono>
std::timed_mutex timed_mtx;
void timedLockExample() {
auto timeout = std::chrono::milliseconds(100);
if (timed_mtx.try_lock_for(timeout)) {
// 在100ms內(nèi)成功獲取鎖
std::this_thread::sleep_for(std::chrono::milliseconds(50));
timed_mtx.unlock();
} else {
// 超時(shí)未能獲取鎖
std::cout << "Could not get the lock within 100ms\n";
}
}2.6std::adopt_lock與std::defer_lock
| 特性 | std::adopt_lock | std::defer_lock |
|---|---|---|
| 用途 | 表示鎖已被當(dāng)前線程獲得 | 表示不立即獲取鎖 |
| 加鎖時(shí)機(jī) | 不嘗試加鎖(假設(shè)已鎖定) | 稍后手動(dòng)加鎖 |
| 典型使用場(chǎng)景 | 與 std::lock 配合使用 | 延遲加鎖或條件加鎖 |
| 可用性 | 適用于 lock_guard 和 unique_lock | 僅適用于 unique_lock |
adopt_lock表示當(dāng)前線程已經(jīng)獲得了互斥鎖的所有權(quán),不需要再嘗試加鎖
#include <mutex>
std::mutex mtx;
void function() {
mtx.lock(); // 手動(dòng)加鎖
// 使用 adopt_lock 告訴 lock_guard 我們已經(jīng)擁有鎖
std::lock_guard<std::mutex> lock(mtx, std::adopt_lock);
// 臨界區(qū)代碼...
// 離開作用域時(shí)自動(dòng)解鎖
}3. 條件變量 (condition_variable)
用于線程間的同步,允許線程等待特定條件成立。
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 等待ready變?yōu)閠rue
std::cout << "Worker is processing data\n";
}
int main() {
std::thread t(worker);
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // 通知等待的線程
t.join();
return 0;
}3.1wait
std::unique_lock<std::mutex> lock(mtx); cv.wait(lock); // 無(wú)條件等待,可能虛假喚醒
帶謂詞的 wait()
cv.wait(lock, []{ return ready; }); // 等價(jià)于:
// while (!ready) {
// cv.wait(lock);
// }wait_for() - 帶超時(shí)等待
using namespace std::chrono_literals;
if (cv.wait_for(lock, 100ms, []{ return ready; })) {
// 條件在超時(shí)前滿足
} else {
// 超時(shí)
}wait_until() - 等待到指定時(shí)間點(diǎn)
auto timeout = std::chrono::steady_clock::now() + 100ms;
if (cv.wait_until(lock, timeout, []{ return ready; })) {
// 條件在時(shí)間點(diǎn)前滿足
} else {
// 超時(shí)
}3.2notify
notify_one() - 通知一個(gè)等待線程
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // 只喚醒一個(gè)等待線程notify_all() - 通知所有等待線程
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_all(); // 喚醒所有等待線程3.3 生產(chǎn)消費(fèi)者模式示例
#include <queue>
#include <chrono>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
const int MAX_SIZE = 10;
void producer() {
for (int i = 0; i < 20; ++i) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return data_queue.size() < MAX_SIZE; });
data_queue.push(i);
std::cout << "Produced: " << i << std::endl;
lock.unlock();
cv.notify_all();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !data_queue.empty(); });
int data = data_queue.front();
data_queue.pop();
std::cout << "Consumed: " << data << std::endl;
lock.unlock();
cv.notify_all();
if (data == 19) break; // 結(jié)束條件
}
}
int main() {
std::thread p(producer);
std::thread c(consumer);
p.join();
c.join();
return 0;
}4. 原子操作 (atomic)
對(duì)于簡(jiǎn)單的數(shù)據(jù)類型,可以使用原子操作避免鎖的開銷。
#include <atomic>
#include <thread>
#include <iostream>
std::atomic<int> counter(0);
void increment_atomic() {
for (int i = 0; i < 100000; ++i) {
++counter; // 原子操作,無(wú)需鎖
}
}
int main() {
std::thread t1(increment_atomic);
std::thread t2(increment_atomic);
t1.join();
t2.join();
std::cout << "Counter: " << counter << "\n";
return 0;
}4.1 基本原子類型
#include <atomic> std::atomic<int> atomicInt(0); // 原子整數(shù) std::atomic<bool> atomicBool(false); // 原子布爾值 std::atomic<long> atomicLong; // 默認(rèn)初始化為0
4.2 加載和存儲(chǔ)
// 存儲(chǔ)值 atomicInt.store(42); // 原子存儲(chǔ) atomicInt = 42; // 等價(jià)寫法 // 加載值 int value = atomicInt.load(); // 原子加載 value = atomicInt; // 等價(jià)寫法
4.3 交換操作
int old = atomicInt.exchange(100); // 原子交換為新值,返回舊值
4.4 讀-修改-寫操作
std::atomic<int> counter(0); // 原子加法,返回舊值 int prev = counter.fetch_add(5); // counter += 5,返回加前的值 // 原子減法 prev = counter.fetch_sub(3); // counter -= 3,返回減前的值
std::atomic<int> flags(0); flags.fetch_or(0x01); // 原子按位或 flags.fetch_and(~0x01); // 原子按位與 flags.fetch_xor(0x03); // 原子按位異或
4.5 比較交換 (CAS)
std::atomic<int> value(10); int expected = 10; // 比較并交換 bool success = value.compare_exchange_weak(expected, 20); // 如果value == expected,則設(shè)置為20,返回true // 否則將expected更新為當(dāng)前value,返回false // 強(qiáng)版本 (較少虛假失敗) success = value.compare_exchange_strong(expected, 30);
4.6 內(nèi)存順序 (Memory Order)
// 默認(rèn)是最嚴(yán)格的內(nèi)存順序 (sequential consistency) atomicInt.store(42, std::memory_order_seq_cst); // 寬松內(nèi)存順序 atomicInt.store(42, std::memory_order_relaxed); // 常見內(nèi)存順序: // - memory_order_relaxed: 無(wú)順序保證 // - memory_order_consume: 數(shù)據(jù)依賴順序 // - memory_order_acquire: 讀操作,防止上方讀寫重排 // - memory_order_release: 寫操作,防止下方讀寫重排 // - memory_order_acq_rel: 讀-修改-寫操作 // - memory_order_seq_cst: 順序一致性 (默認(rèn))
4.7 原子標(biāo)志
std::atomic_flag flag = ATOMIC_FLAG_INIT; // 必須這樣初始化 // 測(cè)試并設(shè)置 (原子操作) bool was_set = flag.test_and_set(); // 清除標(biāo)志 flag.clear();
4.8 原子指針
class MyClass {};
MyClass* ptr = new MyClass();
std::atomic<MyClass*> atomicPtr(ptr);
// 原子指針操作
MyClass* old = atomicPtr.exchange(new MyClass());
// 比較交換指針
MyClass* expected = old;
atomicPtr.compare_exchange_strong(expected, nullptr);4.9 自定義原子類型
struct Point { int x; int y; };
std::atomic<Point> atomicPoint{Point{1, 2}};
// 必須是可平凡復(fù)制的類型(trivially copyable)
static_assert(std::is_trivially_copyable<Point>::value,
"Point must be trivially copyable");
// 原子操作示例
Point old = atomicPoint.load(); // 原子讀取
atomicPoint.store(Point{3, 4}); // 原子寫入
Point newVal{5, 6};
Point expected{3, 4};
atomicPoint.compare_exchange_strong(expected, newVal); // CAS操作std::atomic 對(duì)模板類型 T 的關(guān)鍵要求是:
- 可平凡復(fù)制(Trivially Copyable):保證對(duì)象可以用
memcpy方式安全復(fù)制 - 無(wú)用戶定義的拷貝控制(析構(gòu)函數(shù)、拷貝/移動(dòng)構(gòu)造/賦值)
- 標(biāo)準(zhǔn)布局(Standard Layout)
static_assert 在編譯時(shí)驗(yàn)證這些條件,若不滿足會(huì)立即報(bào)錯(cuò)(比運(yùn)行時(shí)錯(cuò)誤更安全)。
一個(gè)類型 T 是 平凡可復(fù)制(Trivially Copyable) 的,當(dāng)且僅當(dāng)滿足以下所有條件:
- 沒有用戶定義的拷貝構(gòu)造函數(shù)(
T(const T&)) - 沒有用戶定義的移動(dòng)構(gòu)造函數(shù)(
T(T&&)) - 沒有用戶定義的拷貝賦值運(yùn)算符(
T& operator=(const T&)) - 沒有用戶定義的移動(dòng)賦值運(yùn)算符(
T& operator=(T&&)) - 有一個(gè)平凡的(隱式定義的或
=default)析構(gòu)函數(shù) - 所有非靜態(tài)成員和基類也必須是平凡可復(fù)制的
- 不能有虛函數(shù)或虛基類
如果滿足這些條件,編譯器可以安全地使用 memcpy 來(lái)復(fù)制該類型的對(duì)象,而不會(huì)引發(fā)未定義行為(UB)。
5. 死鎖預(yù)防
當(dāng)多個(gè)線程需要多個(gè)鎖時(shí),可能產(chǎn)生死鎖。
預(yù)防方法:
- 總是以相同的順序獲取鎖
- 使用
std::lock同時(shí)鎖定多個(gè)互斥量
std::mutex mtx1, mtx2;
void safe_lock() {
// 同時(shí)鎖定兩個(gè)互斥量,避免死鎖
std::lock(mtx1, mtx2);
std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
// 安全地訪問共享資源
}6. 線程局部存儲(chǔ) (thread_local)
使用 thread_local 關(guān)鍵字聲明線程局部變量,每個(gè)線程有自己的副本。
#include <thread>
#include <iostream>
thread_local int thread_specific_value = 0;
void thread_function(int id) {
thread_specific_value = id;
std::cout << "Thread " << id << ": " << thread_specific_value << "\n";
}
int main() {
std::thread t1(thread_function, 1);
std::thread t2(thread_function, 2);
t1.join();
t2.join();
return 0;
}7. 讀寫鎖
讀寫鎖是一種特殊的同步機(jī)制,允許多個(gè)讀操作并發(fā)執(zhí)行,但寫操作必須獨(dú)占訪問。這種鎖在"讀多寫少"的場(chǎng)景下能顯著提高性能。
C++17 中的std::shared_mutex
#include <shared_mutex>
#include <vector>
class ThreadSafeContainer {
private:
std::vector<int> data;
mutable std::shared_mutex mutex; // mutable 允許const方法加鎖
public:
// 讀操作 - 使用共享鎖
int get(size_t index) const {
std::shared_lock<std::shared_mutex> lock(mutex);
return data.at(index);
}
// 寫操作 - 使用獨(dú)占鎖
void set(size_t index, int value) {
std::unique_lock<std::shared_mutex> lock(mutex);
data.at(index) = value;
}
// 批量讀操作示例
std::vector<int> getSnapshot() const {
std::shared_lock<std::shared_mutex> lock(mutex);
return data;
}
};讀寫鎖特性
三種訪問模式:
- 共享讀鎖 (
shared_lock):多個(gè)線程可同時(shí)持有 - 獨(dú)占寫鎖 (
unique_lock):只有一個(gè)線程可持有 - 升級(jí)鎖 (C++14沒有直接支持,需手動(dòng)實(shí)現(xiàn))
鎖的優(yōu)先級(jí)策略:
- 讀優(yōu)先:容易導(dǎo)致寫線程饑餓
- 寫優(yōu)先:可能降低讀并發(fā)度
- 公平策略:折中方案
典型使用場(chǎng)景:
- 配置信息的熱更新
- 緩存系統(tǒng)
- 高頻查詢低頻修改的數(shù)據(jù)結(jié)構(gòu)
8. 自旋鎖 (Spin Lock)
自旋鎖是一種非阻塞鎖,當(dāng)線程無(wú)法獲取鎖時(shí)不會(huì)休眠,而是循環(huán)檢查鎖狀態(tài)(忙等待)。適用于鎖持有時(shí)間極短的場(chǎng)景。
基本自旋鎖實(shí)現(xiàn)
#include <atomic>
class SpinLock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
while(flag.test_and_set(std::memory_order_acquire)) {
// 可加入CPU暫停指令減少爭(zhēng)用時(shí)的能耗
#ifdef __x86_64__
__builtin_ia32_pause();
#endif
}
}
void unlock() {
flag.clear(std::memory_order_release);
}
bool try_lock() {
return !flag.test_and_set(std::memory_order_acquire);
}
};TTAS + Backoff
class AdvancedSpinLock {
std::atomic<bool> locked{false};
public:
void lock() {
bool expected = false;
int backoff = 1;
const int max_backoff = 64;
while(!locked.compare_exchange_weak(expected, true,
std::memory_order_acquire, std::memory_order_relaxed)) {
expected = false; // compare_exchange_weak會(huì)修改expected
// 指數(shù)退避
for(int i = 0; i < backoff; ++i) {
#ifdef __x86_64__
__builtin_ia32_pause();
#endif
}
backoff = std::min(backoff * 2, max_backoff);
}
}
void unlock() {
locked.store(false, std::memory_order_release);
}
};總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單五子棋游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
linux環(huán)境下C++實(shí)現(xiàn)俄羅斯方塊
這篇文章主要為大家詳細(xì)介紹了linux環(huán)境下C++實(shí)現(xiàn)俄羅斯方塊,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
C/C++中的?Qt?StandardItemModel?數(shù)據(jù)模型應(yīng)用解析
QStandardItemModel?是標(biāo)準(zhǔn)的以項(xiàng)數(shù)據(jù)為單位的基于M/V模型的一種標(biāo)準(zhǔn)數(shù)據(jù)管理方式,本文給大家介紹C/C++中的?Qt?StandardItemModel?數(shù)據(jù)模型應(yīng)用解析,感興趣的朋友跟隨小編一起看看吧2021-12-12
C++實(shí)現(xiàn)控制臺(tái)隨機(jī)迷宮的示例代碼
本文主要介紹了C++實(shí)現(xiàn)控制臺(tái)隨機(jī)迷宮的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08

