日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 單片機 > C語言與CPP編程
[導讀]c++11關于并發(fā)引入了好多好東西,這里按照如下順序介紹: std::thread相關 std::mutex相關 std::lock相關 std::atomic相關 std::call_once相關 volatile相關 std::condition_variable相關 std::future相關 async相關 std::thread相關 c++11之前你可能使用pthr


c++11關于并發(fā)引入了好多好東西,這里按照如下順序介紹:

  • std::thread相關

  • std::mutex相關

  • std::lock相關

  • std::atomic相關

  • std::call_once相關

  • volatile相關

  • std::condition_variable相關

  • std::future相關

  • async相關

std::thread相關

c++11之前你可能使用pthread_xxx來創(chuàng)建線程,繁瑣且不易讀,c++11引入了std::thread來創(chuàng)建線程,支持對線程join或者detach。直接看代碼:

#include <iostream>#include <thread>
using namespace std;
int main() { auto func = []() { for (int i = 0; i < 10; ++i) { cout << i << " "; } cout << endl; }; std::thread t(func); if (t.joinable()) { t.detach(); } auto func1 = [](int k) { for (int i = 0; i < k; ++i) { cout << i << " "; } cout << endl; }; std::thread tt(func1, 20); if (tt.joinable()) { // 檢查線程可否被join tt.join(); } return 0;}

上述代碼中,函數(shù)func和func1運行在線程對象t和tt中,從剛創(chuàng)建對象開始就會新建一個線程用于執(zhí)行函數(shù),調(diào)用join函數(shù)將會阻塞主線程,直到線程函數(shù)執(zhí)行結束,線程函數(shù)的返回值將會被忽略。如果不希望線程被阻塞執(zhí)行,可以調(diào)用線程對象的detach函數(shù),表示將線程和線程對象分離。

如果沒有調(diào)用join或者detach函數(shù),假如線程函數(shù)執(zhí)行時間較長,此時線程對象的生命周期結束調(diào)用析構函數(shù)清理資源,這時可能會發(fā)生錯誤,這里有兩種解決辦法,一個是調(diào)用join(),保證線程函數(shù)的生命周期和線程對象的生命周期相同,另一個是調(diào)用detach(),將線程和線程對象分離,這里需要注意,如果線程已經(jīng)和對象分離,那我們就再也無法控制線程什么時候結束了,不能再通過join來等待線程執(zhí)行完。

這里可以對thread進行封裝,避免沒有調(diào)用join或者detach可導致程序出錯的情況出現(xiàn):

class ThreadGuard { public: enum class DesAction { join, detach };
ThreadGuard(std::thread&& t, DesAction a) : t_(std::move(t)), action_(a){};
~ThreadGuard() { if (t_.joinable()) { if (action_ == DesAction::join) { t_.join(); } else { t_.detach(); } } }
ThreadGuard(ThreadGuard&&) = default; ThreadGuard& operator=(ThreadGuard&&) = default;
std::thread& get() { return t_; }
private: std::thread t_; DesAction action_;};
int main() { ThreadGuard t(std::thread([]() { for (int i = 0; i < 10; ++i) { std::cout << "thread guard " << i << " "; } std::cout << std::endl;}), ThreadGuard::DesAction::join); return 0;}

c++11還提供了獲取線程id,或者系統(tǒng)cpu個數(shù),獲取thread native_handle,使得線程休眠等功能

std::thread t(func);cout << "當前線程ID " << t.get_id() << endl;cout << "當前cpu個數(shù) " << std::thread::hardware_concurrency() << endl;auto handle = t.native_handle();// handle可用于pthread相關操作std::this_thread::sleep_for(std::chrono::seconds(1));

std::mutex相關

std::mutex是一種線程同步的手段,用于保存多線程同時操作的共享數(shù)據(jù)。

mutex分為四種:

  • std::mutex:獨占的互斥量,不能遞歸使用,不帶超時功能

  • std::recursive_mutex:遞歸互斥量,可重入,不帶超時功能

  • std::timed_mutex:帶超時的互斥量,不能遞歸

  • std::recursive_timed_mutex:帶超時的互斥量,可以遞歸使用

拿一個std::mutex和std::timed_mutex舉例吧,別的都是類似的使用方式:

std::mutex:

#include <iostream>#include <mutex>#include <thread>
using namespace std;std::mutex mutex_;
int main() { auto func1 = [](int k) { mutex_.lock(); for (int i = 0; i < k; ++i) { cout << i << " "; } cout << endl; mutex_.unlock(); }; std::thread threads[5]; for (int i = 0; i < 5; ++i) { threads[i] = std::thread(func1, 200); } for (auto& th : threads) { th.join(); } return 0;}

std::timed_mutex:

#include <iostream>#include <mutex>#include <thread>#include <chrono>
using namespace std;std::timed_mutex timed_mutex_;
int main() { auto func1 = [](int k) { timed_mutex_.try_lock_for(std::chrono::milliseconds(200)); for (int i = 0; i < k; ++i) { cout << i << " "; } cout << endl; timed_mutex_.unlock(); }; std::thread threads[5]; for (int i = 0; i < 5; ++i) { threads[i] = std::thread(func1, 200); } for (auto& th : threads) { th.join(); } return 0;}

std::lock相關

這里主要介紹兩種RAII方式的鎖封裝,可以動態(tài)的釋放鎖資源,防止線程由于編碼失誤導致一直持有鎖。

c++11主要有std::lock_guard和std::unique_lock兩種方式,使用方式都類似,如下:

#include <iostream>#include <mutex>#include <thread>#include <chrono>
using namespace std;std::mutex mutex_;
int main() { auto func1 = [](int k) { // std::lock_guard<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_); for (int i = 0; i < k; ++i) { cout << i << " "; } cout << endl; }; std::thread threads[5]; for (int i = 0; i < 5; ++i) { threads[i] = std::thread(func1, 200); } for (auto& th : threads) { th.join(); } return 0;}

std::lock_gurad相比于std::unique_lock更加輕量級,少了一些成員函數(shù),std::unique_lock類有unlock函數(shù),可以手動釋放鎖,所以條件變量都配合std::unique_lock使用,而不是std::lock_guard,因為條件變量在wait時需要有手動釋放鎖的能力,具體關于條件變量后面會講到。

std::atomic相關

c++11提供了原子類型std::atomic<T>,理論上這個T可以是任意類型,但是我平時只存放整形,別的還真的沒用過,整形有這種原子變量已經(jīng)足夠方便,就不需要使用std::mutex來保護該變量啦??匆粋€計數(shù)器的代碼:

struct OriginCounter { // 普通的計數(shù)器 int count; std::mutex mutex_; void add() { std::lock_guard<std::mutex> lock(mutex_); ++count; }
void sub() { std::lock_guard<std::mutex> lock(mutex_); --count; }
int get() { std::lock_guard<std::mutex> lock(mutex_); return count; }};
struct NewCounter { // 使用原子變量的計數(shù)器 std::atomic<int> count; void add() { ++count; // count.store(++count);這種方式也可以 }
void sub() { --count; // count.store(--count); }
int get() { return count.load(); }};

是不是使用原子變量更加方便了呢?

std::call_once相關

c++11提供了std::call_once來保證某一函數(shù)在多線程環(huán)境中只調(diào)用一次,它需要配合std::once_flag使用,直接看使用代碼吧:

std::once_flag onceflag;
void CallOnce() { std::call_once(onceflag, []() { cout << "call once" << endl; });}
int main() { std::thread threads[5]; for (int i = 0; i < 5; ++i) { threads[i] = std::thread(CallOnce); } for (auto& th : threads) { th.join(); } return 0;}

volatile相關

貌似把volatile放在并發(fā)里介紹不太合適,但是貌似很多人都會把volatile和多線程聯(lián)系在一起,那就一起介紹下吧。

volatile通常用來建立內(nèi)存屏障,volatile修飾的變量,編譯器對訪問該變量的代碼通常不再進行優(yōu)化,看下面代碼:

int *p = xxx;int a = *p;int b = *p;

a和b都等于p指向的值,一般編譯器會對此做優(yōu)化,把*p的值放入寄存器,就是傳說中的工作內(nèi)存(不是主內(nèi)存),之后a和b都等于寄存器的值,但是如果中間p地址的值改變,內(nèi)存上的值改變啦,但a,b還是從寄存器中取的值(不一定,看編譯器優(yōu)化結果),這就不符合需求,所以在此對p加volatile修飾可以避免進行此類優(yōu)化。


注意:volatile不能解決多線程安全問題,針對特種內(nèi)存才需要使用volatile,它和atomic的特點如下:
? std::atomic用于多線程訪問的數(shù)據(jù),且不用互斥量,用于并發(fā)編程中
? volatile用于讀寫操作不可以被優(yōu)化掉的內(nèi)存,用于特種內(nèi)存中

std::condition_variable相關

條件變量是c++11引入的一種同步機制,它可以阻塞一個線程或者個線程,直到有線程通知或者超時才會喚醒正在阻塞的線程,條件變量需要和鎖配合使用,這里的鎖就是上面介紹的std::unique_lock。

這里使用條件變量實現(xiàn)一個CountDownLatch:

class CountDownLatch { public: explicit CountDownLatch(uint32_t count) : count_(count);
void CountDown() { std::unique_lock<std::mutex> lock(mutex_); --count_; if (count_ == 0) { cv_.notify_all(); } }
void Await(uint32_t time_ms = 0) { std::unique_lock<std::mutex> lock(mutex_); while (count_ > 0) { if (time_ms > 0) { cv_.wait_for(lock, std::chrono::milliseconds(time_ms)); } else { cv_.wait(lock); } } }
uint32_t GetCount() const { std::unique_lock<std::mutex> lock(mutex_); return count_; }
private: std::condition_variable cv_; mutable std::mutex mutex_; uint32_t count_ = 0;};

關于條件變量其實還涉及到通知丟失和虛假喚醒問題,因為不是本文的主題,這里暫不介紹,大家有需要可以留言。

std::future相關

c++11關于異步操作提供了future相關的類,主要有std::future、std::promise和std::packaged_task,std::future比std::thread高級些,std::future作為異步結果的傳輸通道,通過get()可以很方便的獲取線程函數(shù)的返回值,std::promise用來包裝一個值,將數(shù)據(jù)和future綁定起來,而std::packaged_task則用來包裝一個調(diào)用對象,將函數(shù)和future綁定起來,方便異步調(diào)用。而std::future是不可以復制的,如果需要復制放到容器中可以使用std::shared_future。

std::promise與std::future配合使用

#include <functional>#include <future>#include <iostream>#include <thread>
using namespace std;
void func(std::future<int>& fut) { int x = fut.get(); cout << "value: " << x << endl;}
int main() { std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread t(func, std::ref(fut)); prom.set_value(144); t.join(); return 0;}

std::packaged_task與std::future配合使用

#include <functional>#include <future>#include <iostream>#include <thread>
using namespace std;
int func(int in) { return in + 1;}
int main() { std::packaged_task<int(int)> task(func); std::future<int> fut = task.get_future(); std::thread(std::move(task), 5).detach(); cout << "result " << fut.get() << endl; return 0;}

更多關于future的使用可以看我之前寫的關于線程池和定時器的文章。

三者之間的關系

std::future用于訪問異步操作的結果,而std::promise和std::packaged_task在future高一層,它們內(nèi)部都有一個future,promise包裝的是一個值,packaged_task包裝的是一個函數(shù),當需要獲取線程中的某個值,可以使用std::promise,當需要獲取線程函數(shù)返回值,可以使用std::packaged_task。

async相關

async是比future,packaged_task,promise更高級的東西,它是基于任務的異步操作,通過async可以直接創(chuàng)建異步的任務,返回的結果會保存在future中,不需要像packaged_task和promise那么麻煩,關于線程操作應該優(yōu)先使用async,看一段使用代碼:

#include <functional>#include <future>#include <iostream>#include <thread>
using namespace std;
int func(int in) { return in + 1; }
int main() { auto res = std::async(func, 5); // res.wait(); cout << res.get() << endl; // 阻塞直到函數(shù)返回 return 0;}

使用async異步執(zhí)行函數(shù)是不是方便多啦。

async具體語法如下:

async(std::launch::async | std::launch::deferred, func, args...);

第一個參數(shù)是創(chuàng)建策略:

  • std::launch::async表示任務執(zhí)行在另一線程

  • std::launch::deferred表示延遲執(zhí)行任務,調(diào)用get或者wait時才會執(zhí)行,不會創(chuàng)建線程,惰性執(zhí)行在當前線程。

如果不明確指定創(chuàng)建策略,以上兩個都不是async的默認策略,而是未定義,它是一個基于任務的程序設計,內(nèi)部有一個調(diào)度器(線程池),會根據(jù)實際情況決定采用哪種策略。

若從 std::async 獲得的 std::future 未被移動或綁定到引用,則在完整表達式結尾, std::future的析構函數(shù)將阻塞直至異步計算完成,實際上相當于同步操作:

std::async(std::launch::async, []{ f(); }); // 臨時量的析構函數(shù)等待 f()std::async(std::launch::async, []{ g(); }); // f() 完成前不開始

注意:關于async啟動策略這里網(wǎng)上和各種書籍介紹的五花八門,這里會以cppreference為主。

? 有時候我們?nèi)绻胝嬲龍?zhí)行異步操作可以對async進行封裝,強制使用std::launch::async策略來調(diào)用async。

template <typename F, typename... Args>inline auto ReallyAsync(F&& f, Args&&... params) { return std::async(std::launch::async, std::forward<F>(f), std::forward<Args>(params)...);}
總結





?   std::thread使線程的創(chuàng)建變得非常簡單,還可以獲取線程id等信息。

?   std::mutex通過多種方式保證了線程安全,互斥量可以獨占,也可以重入,還可以設置互斥量的超時時間,避免一直阻塞等鎖。

std::lock通過RAII技術方便了加鎖和解鎖調(diào)用,有std::lock_guard和std::unique_lock。

std::atomic提供了原子變量,更方便實現(xiàn)實現(xiàn)保護,不需要使用互斥量

std::call_once保證函數(shù)在多線程環(huán)境下只調(diào)用一次,可用于實現(xiàn)單例。

volatile常用于讀寫操作不可以被優(yōu)化掉的內(nèi)存中。

std::condition_variable提供等待的同步機制,可阻塞一個或多個線程,等待其它線程通知后喚醒。

std::future用于異步調(diào)用的包裝和返回值。

async更方便的實現(xiàn)了異步調(diào)用,異步調(diào)用優(yōu)先使用async取代創(chuàng)建線程。

關于c++11關于并發(fā)的新特性就介紹到這里

參考資料

https://blog.csdn.net/zhangzq86/article/details/70623394
https://zh.cppreference.com/w/cpp/atomic/atomic
https://zhuanlan.zhihu.com/p/33074506
https://www.runoob.com/w3cnote/c-volatile-keyword.html
https://zh.cppreference.com/w/cpp/thread/async
《深入應用c++11:代碼優(yōu)化與工程級應用》
《Effective Modern C++》




十大經(jīng)典排序算法(動態(tài)演示+代碼)

C語言與C++面試知識總結

數(shù)據(jù)結構之堆棧

一文輕松理解內(nèi)存對齊
一文輕松理解打印有效日志

一文讀懂C語言與C++動態(tài)內(nèi)存

面試中常見的C語言與C++區(qū)別的問題

數(shù)據(jù)結構之線性表

免責聲明:本文內(nèi)容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統(tǒng) 散熱

根據(jù)LED驅動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數(shù)校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉