C 11 多執行緒 併發程式設計總結

2021-09-27 02:48:40 字數 3531 閱讀 3967

建立std::thread,一般會繫結乙個底層的執行緒。若該thread還繫結好函式物件,則即刻將該函式執行於thread的底層執行緒。

執行緒相關的很多預設是move語義,因為在常識中線程複製是很奇怪的行為。

joinable():是否可以阻塞至該thread繫結的底層執行緒執行完畢(倘若該thread沒有繫結底層執行緒等情況,則不可以join)

join():本執行緒阻塞直至該thread的底層執行緒執行完畢。

detach():該thread繫結的底層執行緒分離出來,任該底層執行緒繼續執行(thread失去對該底層執行緒的控制)。

為了避免多執行緒對共享變數的一段操作會發生衝突,引入了互斥體和鎖。

互斥體,一般搭配鎖使用,也可自己鎖住自己(lock(),unlock())。

簡單鎖,構造時請求上鎖,釋放時解鎖,效能耗費較低。適用區域的多執行緒互斥操作。

更多功能也更靈活的鎖,隨時可解鎖或重新鎖上(減少鎖的粒度),效能耗費比前者高一點點。適用靈活的區域的多執行緒互斥操作。

原子變數的意思就是單個最小的、不可分割的變數(例如乙個int),原子操作則指單個極小的操作(例如乙個自增操作)

c++的原子類封裝了這種資料物件,使多執行緒對原子變數的訪問不會造成競爭。(可以利用原子類可實現無鎖設計)

它是乙個原子的布林型別,可支援兩種原子操作。(實際上mutex可用atomic_flag實現)

test_and_set(): 如果atomic_flag物件被設定,則返回true; 如果atomic_flag物件未被設定,則設定之,返回false。

clear():清除atomic_flag物件。

對int, char, bool等基本資料型別進行原子性封裝(其實是特化模板)。

store():修改被封裝的值。

load()讀取被封裝的值

條件變數一般是用來實現多個執行緒的等待佇列,即主線程通知(notify)有活幹了,則等待佇列中的其它執行緒就會被喚醒,開始幹活。

wait(std::unique_lock& lock, predicate pred = ()):pred()為true時直接返回,pred()為false時,lock必須滿足已被當前執行緒鎖定的前提。執行原子地釋放鎖定,阻塞當前執行緒,並將其新增到等待*this的執行緒列表中。

notify_one()/notify_all():啟用某個或者所有等待的執行緒,被啟用的執行緒重新獲得鎖。

處於等待的新增變數可以通過notify_one/notify_all進行喚醒,呼叫函式進行訊號的喚醒時,處於等待的條件變數會重新進行互斥鎖的競爭。

沒有得到互斥鎖的執行緒就會發生等待轉移(wait morphing),從等待訊號量的佇列中轉移到等待互斥鎖的佇列中,一旦獲取到互斥鎖的所有權就會接著向下執行,

但是此時其他執行緒已經執行並重置了執行條件(例如乙個活只需要兩個執行緒來幹,通知完兩個執行緒後重置執行條件),這可能導致該執行緒執行引發未定義的錯誤。

//不能應對虛假喚醒

if(pred())

//利用while重複判斷執行條件,可以應對虛假喚醒

while(pred())

//c++11提供了更方便的語法,將判斷條件作為乙個引數,實際上等價於前者

cv.wait(lock,pred);

構造時,產生乙個未就緒的共享狀態(包含儲存的t值和是否就緒的狀態)。可設定t值,並讓狀態變為ready。

get_future():共享狀態繫結到future物件。

set_value():設定共享狀態的t值,並讓狀態變為ready,則繫結的future物件可get()。

構造時繫結乙個函式物件,也產生乙個未就緒的共享狀態。通過thread啟動或者仿函式形式啟動該函式物件。

但是相比promise,沒有提供set_value()公用介面,而是當執行完繫結的函式物件,其執行結果返回值或所拋異常被儲存於能通過 std::future 物件訪問的共享狀態中。

get_future():共享狀態繫結到future物件。

用於訪問共享狀態(即獲取值)。

當future的狀態還不是ready時就呼叫乙個繫結的promise, packaged_task等的析構函式,會在期望裡儲存乙個異常。

std::future有侷限性,在很多執行緒等待時,只有乙個執行緒能獲取等待結果。

share():分享同乙個共享狀態給另乙個future

wait():若共享狀態不是ready,則阻塞直至ready。

get():獲得共享狀態的值,若共享狀態不是ready,則阻塞直至ready。

當需要多個執行緒等待相同的事件的結果(即多處訪問同乙個共享狀態),需要用std::shared_future來替代std::future。

shared_future與future類似,但shared_future可以拷貝、多個shared_future可以共享某個共享狀態的最終結果(即共享狀態的某個值或者異常)。

shared_future可通過某個future物件隱式轉換,或通過future::share()顯示轉換,無論哪種轉換,被轉換的那個future物件都會變為not-valid

非同步執行乙個函式,其函式執行完後的返還值繫結給使用std::async的futrue(其實是封裝了thread,packged_task的功能,使非同步執行乙個任務更為方便)。

若用建立std::thread執行非同步行為,硬體底層執行緒可能不足,產生錯誤。而std::async將這些底層細節掩蓋住,如果使用預設引數則與標準庫的執行緒管理元件一起承擔執行緒建立和銷毀、避免過載、負責均衡的責任。

所以盡量使用以任務為驅動的async操作設計,而不是以執行緒為驅動的thread設計。

std::async中的第乙個引數是啟動策略,它控制std::async的非同步行為,我們可以用三種不同的啟動策略來建立std::async:

std::launch::async引數保證非同步行為,即傳遞函式將在單獨的執行緒中執行。

std::launch::deferred引數當其他執行緒呼叫get()/wait()來訪問共享狀態時,將呼叫非非同步行為。

std::launch::async | std::launch::deferred引數是預設行為。有了這個啟動策略,它可以非同步執行或不執行,這取決於系統的負載。

///使用示例

std::futureresultfromdb = std::async(std::launch::async, fetchdatafromdb, "data1");

std::string data1 = resultdromdb.get();

C 11併發程式設計 多執行緒std thread

c 11引入了thread類,大大降低了多執行緒使用的複雜度,原先使用多執行緒只能用系統的api,無法解決跨平台問題,一套 平台移植,對應多執行緒 也必須要修改。現在在c 11中只需使用語言層面的thread可以解決這個問題。所需標頭檔案 thread noexcept 乙個空的std thread...

C 11併發程式設計 多執行緒std thread

一 概述 c 11引入了thread類,大大降低了多執行緒使用的複雜度,原jtpbyn先使用多執行緒只能用系統的api,無法解決跨平台問題,一套 平台移植,對應多執行緒 也必須要修改。現在在c 11中只需使用語言層面的thread可以解決這個問題。所需標頭檔案 二 建構函式 1.預設建構函式 2.初...

C 11併發與多執行緒總結

一 執行緒概念 建立及傳參 二 獨佔互斥鎖 mutex,lock guardy與其他mutex 三 unique lock替換lock guardy 四 單例模式 singleton 下的執行緒安全問題 五 window臨界區 六 condition variable條件變數 七 std async...