C++11併發API總結
C++11 多執行緒相關的標頭檔案
- C++ 新標準中引入了四個標頭檔案來支援多執行緒程式設計,他們分別是 < atomic>, < thread>, < condition_variable> 和< future>.
- < atomic>:該標頭檔案主要聲明瞭兩個類,std::atomic和std::atomic_flag,另外還聲明瞭一套C分格的原子型別和與C相容的原操作的函式.
- < thread>:該標頭檔案主要聲明瞭std::thread類,另外std::this_thread名稱空間也在該標頭檔案中.
- < mutex>:該標頭檔案主要申明瞭與互斥量(mutex)相關的類,包括std::mutex系列類,std::lock_guard,std::unique_lock,以及其他型別和函式.
- :該標頭檔案主要聲明瞭與條件變數相關的類,包括std::condition_variable和std::condition_variable_any.
- < future>:該標頭檔案主要申明瞭std::promise,std::package_task兩個Provider類,以及std::future和std::share_future兩個Future類,另外還有一些與之相關的型別和函式,std::async()函式就宣告在此檔案中.
std::thread建構函式
- default thread() noexcept; //noexcept不會丟擲異常,預設建構函式,建立一個空的thread執行物件.
- initialization template
move賦值操作
- move(1) thread& operator=(thread& rhs) noexcept;
- copy delete thread operator(const thread &)=delete;
(1)move 賦值操作,如果當前物件不可joinable,需要傳遞一個右值引用(rhs)給move賦值操作,如果當前物件可被joinable,則terminate()報錯.
(2)拷貝賦值操作被禁用,thread 物件不可被拷貝。
執行緒呼叫到的函式在一個類中,那必須將該函式申明為靜態函式,因為靜態成員函式屬於靜態全域性區,執行緒可以共享這個區域,故可以各自呼叫.
< mutex>標頭檔案介紹
Mutex系列類(4種)
- std::mutex,最基本的Mutex類.
- std::recursive_mutex,遞迴Mutex類.
- std::time_mutex,定時Mutex類.
- std::recursive_timed_mutex,定時遞迴Mutex類.
Lock類(2種)
- std::lock_guard,與Mutex RAII相關,方便執行緒對互斥量上鎖.
- std::unique_lock,與Mutex RAII相關,方便執行緒對互斥量上鎖,但提供了更好的上鎖和解鎖控制.
其他型別
- std::once_flag
- std::adopt_lock_t
- std::defer_lock_t
- std::try_to_lock_t
函式
- std::try_lock,嘗試同時對多個互斥量上鎖.
- std::lock,可以同時對多個互斥量上鎖.
- std::call_once,如果多個執行緒需要同時呼叫某個函式,call_once可以保證多個執行緒對該函式只調用一次.
std::mutex的成員函式
- 建構函式,std::mutex不允許拷貝構造,也不允許move拷貝,最初產生的mutex物件是處於unlockeed狀態的.
- lock(),呼叫執行緒將鎖住該互斥量,執行緒呼叫該函式會發生下面三種情況:(1)如果該互斥量當前沒有被鎖住,則呼叫執行緒將該互斥量鎖住,直到呼叫unlock之前,該執行緒一直擁有該鎖,(2)如果當前的互斥量被其他執行緒鎖住,則當前的呼叫執行緒被阻塞.(3)如果當前互斥量被呼叫執行緒鎖住,則會產生死鎖.
- unlock(),解鎖,釋放對互斥量的所有權.
- try_lock(),嘗試鎖住互斥量如果互斥量被其他執行緒佔有,則當前執行緒也不會被阻塞。執行緒呼叫該函式也會出現下面 3 種情況(1). 如果當前互斥量沒有被其他執行緒佔有,則該執行緒鎖住互斥量,直到該執行緒呼叫 unlock 釋放互斥量。(2). 如果當前互斥量被其他執行緒鎖住,則當前呼叫執行緒返回 false,而並不會被阻塞掉。(3).如果當前互斥量被當前呼叫執行緒鎖住,則會產生死鎖(deadlock)。
幾個與鎖型別相關的Tag類,分別如下:
- std::adopt_lock_t,一個空的標記類,定義如下:struct adopt_lock_t{};該型別的常量物件adopt_lock(adopt_lock是一個常量物件,定義如下:constexpr adopt_lock_t adopt_lock{};通常作為引數傳給unique_lock或lock_guard的建構函式.
- std::defer_lock_t,一個空的標記類,定義如下:struct defer_lock_t{}; 該型別的常量物件defer_lock (defer_lock 是一個常量物件,定義如下: constexpr defer_lock_t defer_lock{}; 通常作為引數傳給unique_lock或lock_guard的建構函式.
- std::try_to_lock_t,一個空的標記類,定義如下: struct try_to_lock_t {}; 該型別常量物件try_to_lock(try_to_lock是一個常量物件,定義如下:constexpr try_to_lock_t try_to_lock{},通常作為引數傳給unique_lock或lock_guard的建構函式.
std::lock_guard介紹
std::lock_gurad 是 C++11 中定義的模板類。定義如下:
template < class Mutex> class lock_guard;
lock_guard物件通常用於管理某個鎖(Lock)物件,在某個lock_guard物件的宣告週期內,它所管理的鎖物件會一直保持上鎖狀態,而lock_guar=生命週期結束後,它所管理的鎖物件會被解鎖. 模板Mutex代表互斥量型別,例如std::mutex型別,它應該是一個基本的BasicLockable型別
* 在 lock_guard 物件構造時,傳入的 Mutex 物件(即它所管理的 Mutex 物件)會被當前執行緒鎖住。在lock_guard 物件被析構時,它所管理的 Mutex 物件會自動解鎖,由於不需要程式設計師手動呼叫 lock 和 unlock 對 Mutex 進行上鎖和解鎖操作,因此這也是最簡單安全的上鎖和解鎖方式,尤其是在程式丟擲異常後先前已被上鎖的 Mutex 物件可以正確進行解鎖操作,極大地簡化了程式設計師編寫與 Mutex 相關的異常處理程式碼。
lock_guard 建構函式
locking(1) explicit lock_guard(mutex_type& m);
adopting(2) lock_guard(mutex_type& m,adopt_lock_t tag);
copy deleted lock_guard(const lock_guard&)=delete;
1.locking初始化:
lock_guard 物件管理Mutex物件m,並在構造時對m進行上鎖(呼叫m.lock()).
2.adopting初始化
lock_guard物件管理Mutex物件m,與locking初始化(1)不同的是,Mutex物件m已被當前執行緒鎖住.
3.拷貝構造
lock_guard物件的拷貝構造和移動構造都被禁用,因此lock_guard物件不可被拷貝構造或移動構造.
std::unique_lock介紹
- unique_lock 物件以獨佔所有權的方式管理 mutex 物件的上鎖和解鎖操作就是沒有其他的 unique_lock 物件同時擁有某個 mutex 物件的所有權。
- 在構造(或移動(move)賦值)時,unique_lock 物件需要傳遞一個 Mutex 物件作為它的引數,新建立的 unique_lock 物件負責傳入的 Mutex 物件的上鎖和解鎖操作.
unique_lock 不同於lock_guard的用法.
unique_lock的建構函式
- default(1) unique_lock noexcept;
- locking(2) explicit unique_lock(mutex_type & m);
- try-locking(3) unique_lock(mutex_type& m,try_to_lock_t tag);
- deferred(4) unique_lock(mutex_tyoe& m,defer_lock_t tag) noexcept;
- adopting(5) unique_locking(mutex_type& m,adopt_lock_t tag);
- locking for(6) template
unique_lock移動賦值操作
- move(1) unique_lock& operator=(unique_lock&& x)noexcept;
- copy deleted unique_lock& operator=(const unique_lock&)=delete;
- 移動賦值之後,由x所管理的Mutex物件及其狀態將會被新的unique_lock物件取代.
- 如果被賦值的物件之前已經獲得了它所管理的Mutex物件的鎖,則在移動賦值之前會呼叫unlock函式釋放它所佔有的鎖.
- 呼叫移動賦值之後,x物件如同通過預設建構函式所建立的,也就不再管理任何Mutex物件了. ### unique_lock主要成員函式
(1)上鎖/解鎖操作:lock,try_lock,try_lock_for,try_lock_until和unlock.
(2)修改操作:移動賦值,交換(swap)(與另外一個unique_lock物件交換他們所管理的Mutex物件的所有權),釋放(release)(返回指向它所管理的Mutex物件的指標).
(3)獲取屬性操作:owns_lock(返回當前unique_lock物件是否獲得了鎖),operator bool()(與owns_lock功能相同,返回當前unique_lock物件是否獲得了鎖,mutex(返回當前unique_lock物件所管理的Mutex物件的指標).
unique_lock::lock
該函式返回時,當前的unique_lock物件便擁有了它所管理的Mutex物件的鎖,如果上鎖失敗,則丟擲system_error異常.
unique_lock::try_lock
上鎖操作,呼叫它所管理的Mutex物件的try_lock函式,如果上鎖成功,返回true,否則返回false.
std::unique_lock::try_lock_for
上鎖操作,呼叫它所管理的Mutex物件的try_lock_for函式,上鎖成功,返回true,否則返回false.
std::unique_lock::release
返回指向所管理的Mutex物件的指標,並釋放所有權.
std::unique_lock::owns_lock
返回當前std::unique_lock物件是否獲得了鎖.
標頭檔案介紹
- Provides類:std::promise,std::package_task
- Futures類:std::future,shared_future.
- Provides函式:std::async()
- 其他型別:std::future_error,std::future_errc,std::future_status,std::launch.
std::promise類介紹
- promise物件可以儲存某一型別T的值,該值可以被future物件讀取(可能在另外一個執行緒中),因此promise也提供了一種執行緒同步的手段,在promise物件構造時可以和一個共享狀態(通常是std::future)相關聯,並可一在相關聯的共享狀態(std::future)上儲存一個型別為T的值.
- 可以通過get_future來獲取與該promise物件相關聯的future物件,呼叫該函式之後,兩個物件共享相同的共享狀態.
- promise物件是非同步Provider,它可以在某一時刻設定共享狀態的值.
- future物件可以非同步返回共享狀態的值,或者在必要的情況下阻塞呼叫者並等待共享狀態標誌為ready,然後才能獲取共享狀態的值 .
std::promise建構函式
- default(1) promise();
- with allocator(2) template promise(allocator_arg_t aa,const Alloc& alloc);
- copy deleted promise(const promise&)=delete;
- move(4) promise(promise&& x) noexcept;
(1)預設建構函式,初始化一個空的共享狀態,
(2)帶自定義記憶體分配器的建構函式,與預設建構函式類似,使用自定義分配器來分配共享狀態
(3)拷貝建構函式,被禁用.
(4)移動建構函式.
std::promise::get_future介紹
- 該函式返回一個與promise共享狀態相關聯的future,返回的future物件可以訪問由promise物件設定在共享狀態上的值或者某個異常物件,只能從promise共享狀態獲取一個future物件,在呼叫該函式之後,promise物件通常會在某個時間點準備好(設定一個值或者一個異常物件),如果不設定值或異常,promise物件在析構時會自動設定一個future_error異常.
- std::promise::set_value介紹
generic template(1) void set_value(const T& val); void set_value(T&& val);
specializations(2) void promise
std::promise::set_value_at_thread_exit 介紹
*設定共享狀態的值,但是不將共享狀態的標誌設定為 ready,當執行緒退出時該 promise 物件會自動設定為 ready。如果某個 std::future 物件與該 promise 物件的共享狀態相關聯,並且該 future 正在呼叫 get,則呼叫 get 的執行緒會被阻塞,當執行緒退出時,呼叫 future::get 的執行緒解除阻塞,同時 get 返回 set_value_at_thread_exit 所設定的值。注意,該函式已經設定了 promise 共享狀態的值,如果線上程結束之前有其他設定或者修改共享狀態的值的操作,則會丟擲 future_error( promise_already_satisfied )。
std::promise::swap介紹
交換promise的共享狀態.
std::package_task
- std::packaged_task**包裝一個可呼叫的物件**,並且允許非同步獲取該可呼叫物件產生的結果,從包裝可呼叫物件意義上來講,std::package_task與std::function類似,只不過std::packaged_task將其包裝的可呼叫物件的執行結果傳遞給一個std::future物件(該物件通常在另外一個執行緒中獲取std::packaged_task任務的執行結果).
- std::packaged_task物件內部包含兩個最基本元素,一.被包裝的任務,任務是一個可呼叫的物件,如函式指標,成員函式指標或函式物件,二.共享狀態(shared state),用於儲存任務的返回值,可以通過std::future物件來達到非同步訪問共享狀態的效果.
- 可以通過std::packsged_task::get_future來獲取與共享狀態相關聯的std::future物件,在呼叫該函式之後,兩個物件共享相同的共享狀態:
- std::packaged_task物件是非同步Provider,它是某一時刻通過呼叫被包裝的任務來設定共享狀態的值.
- std::future物件是一個非同步返回物件,通過它可以獲得共享狀態的值,當然在必要的時候需要等待共享狀態變為ready;
- std::packaged_task的共享狀態的生命週期一直持續到最後一個與之關聯的物件被釋放或者銷燬為止.
std::packaged_task 建構函式
default(1) package_task() noexcept;
initialization(2) template explicit packaged_task (Fn &&fn);
with allocator(3) template
std::packaged_task::valid 介紹
檢查當前packaged_task是否和一個有效的共享狀態相關聯,對於由預設建構函式生成的package_task物件,該函式返回false,除非中間進行了move賦值操作或者swap操作.
std::packaged_task::operator()(Args…..args)介紹
- 呼叫該packaged_task物件所包裝的物件(通常為函式指標,函式物件,lambda表示式等),傳入的引數為args,呼叫該函式一般會發生兩種情況:
- 如果成功呼叫packaged_task所包裝的物件,則返回值(如果包裝的物件有返回值的話)被儲存在packaged_task的共享狀態中.
- 如果呼叫package_task所包裝的物件失敗,並且丟擲了異常,則異常也會被儲存在packaged_task的共享狀態中,呼叫失敗,並且丟擲了異常,則異常也會被儲存在package_task的共享狀態中.
std::packaged_task::make_ready_at_thread_exit 介紹
- 該函式會呼叫被包裝的任務,並向任務傳遞引數,類似 std::packaged_task 的 operator() 成員函式。但是與 operator() 函式不同的是,make_ready_at_thread_exit 並不會立即設定共享狀態的標誌為 ready,而是線上程退出時設定共享狀態的標誌。
- 如果與該 packaged_task 共享狀態相關聯的 future 物件在 future::get 處等待,則當前的 future::get 呼叫會被阻塞,直到執行緒退出。而一旦執行緒退出,future::get 呼叫繼續執行,或者丟擲異常。
std::package_task::reset()介紹
重置packaged_task的共享狀態,但是保留之前的被包裝的任務.使得packaged_task 物件被重用.
std::packaged_task::swap() 介紹
交換 packaged_task 的共享狀態。
future介紹
- std::future可以用來獲取非同步任務的結果,因此可以把它當成一種簡單的執行緒間同步的手段. future通常由某個Provider建立,我們可以把Provider想象成一個非同步任務的提供者,Provider在某個執行緒中設定共享狀態的值,與該共享狀態相關聯的future物件呼叫get(通常在另一個執行緒中)獲取該值,如果當前的共享狀態部位ready,則std::future::get會阻塞當前的呼叫執行緒.直到Provider設定了當前共享狀態的值,get方法返回非同步任務的值或異常.
- 一個有效的std::future物件通常由以下三種Provider建立,並和某個共享狀態相關聯,Provider可以是函式,也可以是類:
- std::async()函式.
- std::promise::get_future,此時get_future為promise類的成員函式.
- std::packaged_task,此時get_future為package_task類的成員函式.
- 一個 std::future 物件只有在有效(valid)的情況下才有用(useful),由 std::future 預設建構函式建立的 future 物件不是有效的(除非當前非有效的 future 物件被 move 賦值另一個有效的 future 物件)。
std::future建構函式
- std::future一般由std::async,std::promise::get_future,std::packaed_task::get_future建立,不過也提供了建構函式:
- default(1) future() noexcept;
- copydeleted future(const future&)=delete;
- move(3) future(future&& y)noexcept;
std::future<int> fut; //預設建構函式
fut=std::async(do_some_task);
std::future::share()
返回一個std::shared_future物件(本文後續內容將介紹std::shared_future);呼叫該函式之後,該std::future物件本身已經不和任何共享狀態相關聯,因此該std::future的狀態不再是valid的了.
std::future::get()
std::future::get一共有三種形式:
(1) generic template(1) T get(); 返回一個值.
(2) reference specialization R& future
std::future::valid()
- 檢查當前的std::future物件是否有效,即釋放與某個共享狀態相關聯,一個有效的std::future物件只能通過std::async(),std::promise::get_future或者std::packaged_task::get_future來初始化.由std::future預設建構函式建立的std::future物件是無效的,通std::future的move賦值後該std::future物件也可以變為valid.
std::future::wait()
- 等待與當前std::future物件相關聯的共享狀態的標誌變為ready;
- 如果共享狀態的標誌不是ready(此時Provider沒有在共享狀態上設定值或者異常),呼叫該函式會被阻塞當前執行緒,直到共享狀態的標誌變為ready,一旦共享狀態變為ready,wait()函式返回,當前執行緒被解除阻塞,但是wait()並不讀取共享狀態的值或者異常;
std::future::wait_for()
與std::future::wait()的功能類似,即等待與該std::future物件相關聯的共享狀態變為ready,該函式的原型如下:
template<class Rep,class Period> future_status wait_for(const chrono::duration<Rep,Period>& rel_time)const;
而與std::future::wait()不同的是,wait_for()可以設定一個時間段rel_time,如果共享的標誌在該時間段結束之前沒有被Provider設定為ready,則呼叫wait_for的執行緒被阻塞,在等待了rel_time的時間長度後wait_until()返回.
* future_status::ready:共享的狀態已經變為ready,即Provider在共享狀態上設定了值或異常.
* future_status::timeout:超時,即在規定的時間內共享狀態的標誌沒有變為ready.
* future_status::deffered:共享狀態包含一個deferred函式.
std::future::wait_until()
wait_until()可以設定一個系統絕對時間,如果共享狀態的標誌在該時間點到來之前沒有被Provider設定為ready,則呼叫wait_until的執行緒阻塞. ### std::shared_future介紹
std::sahred_future與std::future類似但是std::shared_future可以拷貝,多個std::shared_future可以共享某個共享狀態的最終結果(即共享狀態的某個值或者異常),shared_future可以通過某個std::future物件隱式轉換(std::shared_future的建構函式 ),或者通過future::shared()顯式轉換,無論哪種轉換,被轉換的那個std::future物件都會變為not-valid.
std::shared_future建構函式.
(1) default(1) shared_future() noexcept;
(2)copy(2) shared_future(const shared_future& x);
(3)move(3) shared_future(shared_future&& x)noexcept;
(4)move from future(4) shared_future(future&& x) noexcept;
最後move form future(4) 即從一個有效的std::future物件構造一個std::shared_future,構造之後std::future物件x變為無效.
std::async()介紹
(1) unspecified policy(1) template
與std::future相關的列舉類介紹
- enum class future_errc;
- enum class future_status;
- enum calss launch;
future_errc類不長用,
std::launch型別
- launch::async:非同步任務會在另外一個執行緒中呼叫,並通過共享狀態返回非同步任務的結果.
條件變數標頭檔案裡的類和函式介紹.
標頭檔案主要包含了與條件變數相關的類和函式,相關的類包括std::condition_variable和std::condition_variable_any,還有列舉型別std::cv_status,另外還包括函式std::notify_all_thread_exit(),下面介紹以上幾種型別.
condition_variable類介紹
- std::condition_variable是條件變數,Linux下使用Pthread庫中的pthread_cond_*()函式提供了與條件變數相關的功能.
- 當std::condition_variable物件的某個wait函式被呼叫的時候,它使用std::unique_lock(通過std::mutex)來鎖住當前執行緒,當前執行緒會一直被阻塞,直到另外一個執行緒在相同的std::condition_variable物件上呼叫了notification函式來喚醒當前執行緒.
- std::condition_variable物件通常使用std::unique_lock來等待,如果需要使用另外的lockable型別,可以使用std::condition_variable_any類.
std::condition_variable建構函式
default(1) condtion_variable();
copydeleted condition_variable(const condition_variable&)=delete;
condition_variable的拷貝建構函式被禁用,只提供了預設建構函式.
std::condition_variable::wait()介紹
- (絕對的)uncondition(1) void wait(unique_lock& lck);
- predicate(2) template void wait(unique_lock& lck,Predicate pred);
- std::condition_variable提供了兩種wait()函式,當前執行緒呼叫wait()後將被阻塞(此時當前執行緒應該獲得了鎖(mutex)),不妨設獲得鎖lck,直到另外某個執行緒呼叫notify_*喚醒當前執行緒.
- 線上程被阻塞時,該函式會自動呼叫lck.unlock()釋放鎖,使得其他被阻塞在鎖競爭上的執行緒得以繼續執行,另外,一旦當前執行緒獲得通知(notified,通常是另外某個執行緒呼叫notify_*喚醒當前執行緒),wait()函式也是自動呼叫lck.lock(),使得lck的狀態和wait函式被呼叫時相同.
- 在第二種情況下(即設定了Predicate),只有當pred條件為fasle時呼叫wait()才會阻塞當前執行緒,並且在收到其他執行緒的通知後只有當pred為true時才會被解除阻塞,因此第二種情況類似以下程式碼.
while(!pred()) wait(lck);
std::condition_variable::wait_for()介紹
與std::condition_variable::wait()類似,不過wait_for可以指定一個時間段,在當前執行緒收到通知或者指定的時間rel_time超時之前,該執行緒都會處於阻塞狀態,而一旦超時或者收到裡其他執行緒的通知,wait_for返回,剩下的處理步驟和wait()類似.
std::condition_variable::wait_until()介紹
與wait_for()類似,不過wait_until可以指定一個時間點.
std::condition_variable::notify_one()介紹
喚醒某個等待執行緒,如果當前沒有等待執行緒,則該函式什麼也不做,如果同時存在多個等待執行緒,則喚醒某個執行緒是不確定的.
std::condition_variable::notify_all()介紹
喚醒所有等待的執行緒,如果沒有等待的執行緒,則不做什麼.
std::condition_variable_any 介紹
與std::condition_variable類似,只不過std::condition_variable_any的wait函式可以接受任何lockable引數.而std::condition_variable只能接受std::unique_lock型別的引數
cv_status列舉型別介紹
- cv_status::no_timeout wait_for或者wait_until沒有超時,即在規定的時間內執行緒接受到通知.
- cv_status::timeout wait_for或者wait_until超時.
notify_all_at_thread_exit .
void notify_all_at_thread_exit(condition_variable& cond,unique_lock<mutex> lck);
當呼叫該函式的執行緒退出時,所有在cond條件變數上等待的執行緒都會收到通知.
atomic型別詳解–atomic_flag介紹
- 標頭檔案中最簡單的原子型別:atomic_flag,atomic_flag一種簡單的原子布林型別,只支援兩種操作,test-and-set和clear.
std::atomic_flag建構函式
atomic_flag() noexcept=default;
atomic_flag(const atomic_flag& T)=delete;
- std::atomic_flag只有預設建構函式,拷貝建構函式已被禁用,因此不能從其他std::atomic_flag物件構造一個新的std::atomic_flag物件.
- 如果在初始化時沒有明確使用ATOMIC_FLAG_INIT初始化,那麼新建立的std::atomic_flag物件的狀態是未指定的(既沒有被set也沒有被clear),另外,atomic_flag不能被拷貝,也不能move賦值.
- ATOMIC_FLAG_INIT:如果某個std::atomic_flag物件使用該巨集初始化,那麼可以保證該std::atomic_flag物件在建立時處於clear狀態.
atomic_flag::test_and_set 介紹
函式原型:
bool test_and_set(memory_order sync = memory_order_seq_cst)
volatile noexcept;
bool test_and_set(memory_order sync=memory_order_seq_cst);
- test_and_set()函式檢查std::atomic_flag標誌,如果std::atomic_flag之前沒有被設定過,則設定std::atomic_flag的標誌,並返回先前該std::atomic_flag物件是否被設定過.如果已經被設定過,則返回true,否則返回false.
test_and_set引數sync的取值.
Memory Order值 | Memory Order型別 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memeory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
std::atomic_flag::clear()介紹
清除std::atomic_flag物件的標誌位,即設定atomic_flag的值為false,clear函式的原型如下:
void clear(memory_order sync = memory_order_seq_cst) volatile noexcept;
void clear(memory_order sync = memory_order_seq_cst) noexcept;
清除std::atomic_flag標誌使得下一次呼叫std::atomic_flag::test_and_set返回false;
std::atomic介紹
std::atomic是模板類,一個模板型別為T的原子物件中封裝了一個型別為T的值.
template<class T> struct
atomic;
原子型別物件的主要特點就是從不同執行緒訪問不會導致資料競爭. 因此從不同執行緒訪問某個原子物件是良性行為,而通常對於非原子型別而言,併發的訪問某個物件(如果不做任何同步操作)會導致未定義的行為發生. std::atomic成員函式
std::atmoic的建構函式如下:
default(1) atmoic() noexcept=default;
initialization(2) constexpr atomic(T val) noexcept;
copydeleted atomic(const atomic&)=delete;
1.預設建構函式,由預設建構函式建立的std::atomic物件處於未初始化狀態,對於為初始化的std::atomic物件可以由atomic_init函式進行初始化.
2.初始化建構函式,由型別T初始化一個std::atomic物件.
3.拷貝建構函式被禁用.
std::atomic::operator=()函式
std::atomic的賦值操作函式定義如下:
set value(1) T operator=(T val) noexcept;
T operator=(T val)volatile noexcept;
copydeleted;
普通的賦值拷貝操作已經被禁用,但是一個型別為T的變數可以賦值給相應的原子型別變數(相當於隱式轉換),該操作是原子的,記憶體序預設順序一致性(memory_order_seq_cst),如果需要指定其他的記憶體序,需要使用std::atomic::store();
is_lock_free
bool is_lock_free() const volatile noexcept;
bool is_lock_free() const noexcept;
判斷std::atomic物件是否具備lock-free的特性,如果某個物件滿足lock-free特性,在多個執行緒訪問該物件時不會導致執行緒阻塞,
store
函式原型:
void store(T val,memory_order sync = memory_order_seq_cst) volatile noexcept;
void store(T val,memory_order sync = memory_order_seq_cst) noexcept; 修改被封裝的值,std::atomic::store函式將型別為T的引數val複製給原子物件所封裝的值,T是std::atomic類模板引數,另外引數sync指定記憶體序.
Memory Order 值 | Memory Order型別 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_release | Release |
memory_order_seq_cst | Sequentially consistent |
load
T load(memory_order sync=memory_order_seq_cst) const volatile noexcept;
T load(memory_order sync=memory_order_seq_cst) cosnt noexcept;
operatorT() const volatile noexcept;
operator T()const noexcept;
與load功能類似,也是讀取被封裝的值,operator T()是型別轉換操作,預設的記憶體序是std::memory_order_seq_cst,如果需要指定其他的記憶體序,應該使用load()函式;
exchange
T exchange(T val,memory_order sync = memory_order_seq_cst) volatile noexcept;
T exchange(T val,memory_order_sync = memory_order sync =memory_order_seq_cst) noexcept;
讀取並修改被封裝的值,exchange會將val指定的值替換掉之前該原子物件封裝的值,並返回之前該原子物件封裝的值,整個過程是原子的.
記憶體序(簡單介紹一下,前面用到了,知道記憶體序是為了約束記憶體操作的就行,具體的記憶體模型以後再學習)
c++11中中定義了以下6種語義來對記憶體操作的行為進行約束,這些語義分別規定了不同的記憶體操作在其他執行緒中的可見性問題.
enum memory_oeder{
memory_order_relaxed, //保證是原子操作,但是不保證優化後的程式碼順序.
memory_order_consume, //與運算元相關的才會有效.
memory_order_acquire, //不僅是從記憶體讀,還保證了記憶體程式碼執行的順序,這句程式碼必須在這句程式碼後面的程式碼執行前執行.
memory_order_release, //確保寫入記憶體,但同時也保證了:這句程式碼必須在執行完這句程式碼前面的程式碼之後執行.
memory_order_acq_rel, //看一下exchange吧,相當於acquire和release;
memory_order_seq_cst //不允許編譯器調整語句順序,
};