C++多线程编程的几种实现方式小结
文章目錄
- 前言
- 一、互斥鎖
- 1.mutex
- 2.lock_guard
- 3.unique_lock
- 二、條件變量
- condition_variable
- 三、信號(hào)量
- semaphore
- 四、異步操作
- 1.async構(gòu)造方式
- 2.future
- 3.promise
- 五、原子操作
- 備注
前言
關(guān)于C++多線程編程的幾種實(shí)現(xiàn)方式(互斥鎖、條件變量、信號(hào)量、異步操作、原子操作)小結(jié)
一、互斥鎖
使用鎖的方式對共享資源對象的訪問進(jìn)行控制,操作包括上鎖lock()、解鎖unlock()
1.mutex
- include <mutex>
- lock和unlock必須成對出現(xiàn)
- 構(gòu)造出來的mutex對象都是unlock狀態(tài)
- mutex不允許拷貝構(gòu)造
- 當(dāng)前線程lock了mutex,該線程在unlock之前一直擁有該鎖,被鎖期間阻塞。如果是自身線程調(diào)用了lock鎖住mutex,可能會(huì)產(chǎn)生死鎖
- try_lock(),嘗試鎖,即使被占有也不會(huì)阻塞(失敗會(huì)返回false)
- mutex為最簡單的互斥鎖,不支持遞歸上鎖,需要遞歸上鎖要使用recursive_lock類
2.lock_guard
- 構(gòu)造方式:lock_guard<mutex> lck(mtx);
- 相比較mutex最大特點(diǎn)是,構(gòu)造時(shí)自動(dòng)lock,離開作用域后調(diào)用析構(gòu)函數(shù)unlock,可以與智能指針類比
- 采用資源請求初始化(RAII)編程技術(shù),針對動(dòng)態(tài)分配的資源(需要申請內(nèi)存,非static對象)
- 不能手動(dòng)解鎖,不能拷貝
3.unique_lock
- 比lock_guard功能更加強(qiáng)大,但開銷也大
- 構(gòu)造時(shí)選擇加鎖方式:defer_lock延遲加鎖;try_lock嘗試加鎖;adopt_lock立刻加鎖
- release()函數(shù),返回管理的mutex對象指針,釋放所有權(quán)(不能再對該mutex進(jìn)行控制),之后用戶可以對mutex進(jìn)行手動(dòng)unlock操作,此功能給予更大的編程自由度,從而完成相應(yīng)功能
- 配合condition_variable使用,condition_variable wait()函數(shù)第一個(gè)參數(shù)為unique_lock類型
- 不允許復(fù)制,但允許"move",即對mutex的所有權(quán)進(jìn)行傳遞,lck1對mymutex所有權(quán)轉(zhuǎn)移至lck2:
unique_lock<mutex>lck1(mymutex);
unique_lock<mutex> lck2(mymutex);
二、條件變量
條件不滿足,線程被阻塞。一個(gè)線程等待某一變量的成立而阻塞,另一線程使該變量變化而使上一線程成立,同時(shí)發(fā)送信號(hào)(notify)喚醒wait的線程
condition_variable
- 獲取mutex,確切的說為mutex構(gòu)造的unique_lock對象
- wait_for(),wait_until(),第二個(gè)參數(shù)為chrono(時(shí)間庫)
- wait_for(),wait()條件成立 or 超過給定時(shí)間段的時(shí)間自動(dòng)unlock
- wait_until(),同上,但時(shí)間段改為時(shí)刻
- notify_all(),notify_one(),全部喚醒 or 喚醒之一
三、信號(hào)量
提供原子操作,C語言提供
semaphore
- include <semaphore.h>
- 定義:sem_t mysem;
- 初始化:int sem_init(sem_t *sem,int pshared,unsigned int value); value 參數(shù)指定信號(hào)量的初始值。一般:sem_init(&mysem,0,0);需要首次執(zhí)行的:sem_init(&mysem,0,1);
pshared 參數(shù)指明信號(hào)量是由進(jìn)程內(nèi)線程共享,還是由進(jìn)程之間共享。如果 pshared 的值為 0,那么信號(hào)量將被進(jìn)程內(nèi)的線程共享,并且應(yīng)該放置在這個(gè)進(jìn)程的所有線程都可見的地址上(如全局變量,或者堆上動(dòng)態(tài)分配的變量)。
- int sem_wait(sem_t *sem); //sem+1 >0時(shí)終端阻塞,繼續(xù)執(zhí)行
- int sem_post(sem_t *sem); //sem-1
- int sem_trywait(sem_t *sem);
- int sem_getvalue(sem_t *sem);
四、異步操作
在不需要等待被調(diào)用方返回之前,繼續(xù)進(jìn)行后續(xù)操作。c++11提供了異步接口std::async,std::async會(huì)自動(dòng)創(chuàng)建一個(gè)線程去調(diào)用 線程函數(shù),它返回一個(gè)std::future,這個(gè)future中存儲(chǔ)了線程函數(shù)返回的結(jié)果,當(dāng)我們需要線程函數(shù)的結(jié)果時(shí),直接從future中獲取
1.async構(gòu)造方式
-
template <class Fn, class… Args> future<typename
result_of<Fn(Args…)>::type>
async (Fn&& fn, Args&&… args); -
template <class Fn, class… Args> future<typename
result_of<Fn(Args…)>::type>
async (launch policy, Fn&& fn, Args&&… args); -
policy:
launch::async: 異步啟動(dòng)一個(gè)新線程調(diào)用fn
launch::deferred延遲:對 fn 的調(diào)用被延遲,直到返回的未來的共享狀態(tài)被訪問(等待或獲取)。 此時(shí),調(diào)用 fn 并且該函數(shù)不再被視為延遲。 當(dāng)此調(diào)用返回時(shí),返回的未來的共享狀態(tài)已準(zhǔn)備就緒。
launch::async|launch::deferred:該函數(shù)自動(dòng)選擇策略(在某一點(diǎn))。這取決于系統(tǒng)和庫實(shí)現(xiàn),它們通常針對系統(tǒng)中當(dāng)前的并發(fā)可用性進(jìn)行優(yōu)化。 -
fn 函數(shù)指針,可以為仿函數(shù)、lambda表達(dá)式,類的成員函數(shù)等
-
args 傳遞給fn的參數(shù)
2.future
- future是一個(gè)可以從某個(gè)提供者對象或函數(shù)中檢索值的對象,如果在不同的線程中,可以正確地同步這種訪問
- template future;
template <class R&> future<R&>;
template <> future<void>; - future是與共享狀態(tài)相關(guān)聯(lián)的未來對象,并且通過調(diào)用以下函數(shù)之一來構(gòu)造:
async
promise::get_future
packaged_task::get_future - get()取值、valid()共享是否有效、wait()阻塞等待(由valid判斷,false阻塞,true繼續(xù)執(zhí)行)、wait_for()、wait_until()同上
3.promise
- promise是一個(gè)對象,它可以存儲(chǔ)一個(gè)T類型的值,供將來的對象(可能在另一個(gè)線程中)檢索,提供一個(gè)同步點(diǎn)。
- 這個(gè)共享狀態(tài)可以通過調(diào)用成員get_future關(guān)聯(lián)到一個(gè)未來對象。調(diào)用后,兩個(gè)對象共享相同的共享狀態(tài): promise對象是異步提供者,應(yīng)該在某個(gè)時(shí)候?yàn)楣蚕頎顟B(tài)設(shè)置一個(gè)值。 future對象是一個(gè)異步返回對象,它可以檢索共享狀態(tài)的值,并在必要時(shí)等待它準(zhǔn)備就緒。
- set_value()設(shè)置value并通知future
五、原子操作
原子操作能夠保證多個(gè)線程順序訪問,不會(huì)導(dǎo)致數(shù)據(jù)爭用,執(zhí)行時(shí)沒有任何其它線程能夠修改相同的原子對象。原子操作更加接近底層,因而效率更高。
- std::atomic<T>構(gòu)造對象
- atomic_long total(0);
- atomic<bool> flag{ false }
- 底層原理:自旋鎖 + CAS(樂觀鎖)
- 樂觀鎖:更新值時(shí)進(jìn)行compareAndSwap操作,即當(dāng)內(nèi)存當(dāng)前值等于內(nèi)存預(yù)期值時(shí),將要更新的值賦給內(nèi)存,不等時(shí)不操作,之后可重試
- 自旋鎖:嘗試獲取鎖的所有權(quán)時(shí)會(huì)以忙等待的形式不斷的循環(huán)檢查鎖是否可用
備注
以上為實(shí)際遇到的問題,待補(bǔ)充
總結(jié)
以上是生活随笔為你收集整理的C++多线程编程的几种实现方式小结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: H5页面 点击按钮播放视频,默认全屏播放
- 下一篇: 【GAMS与C++的交互】