c++11執行緒基本用法
-
簡單示例
-
thread和join()
#include<iostream> #include<thread> using namespace std; void test() { cout << "我的執行緒執行" << endl; for (int i = 0; i < 10; ++i); cout << "我的執行緒執行完畢" << endl; } int main() { //thread是標準庫的類,test為可呼叫物件,作為執行緒執行起點 thread th(test); //join():阻塞主執行緒,讓主執行緒等待子執行緒執行完畢 th.join(); cout << "主執行緒執行結束" << endl; return 0; }
-
detach()
//傳統多執行緒程式主執行緒要等待子執行緒執行完畢后才能退出 //detach():將主執行緒和子執行緒分離,可以讓主執行緒不必等待子執行緒 //一旦detach()之后,與主執行緒關聯的thread物件就會視區和主執行緒的關聯,此時這個子執行緒就會在后臺運行,當子執行緒執行完成后,由語和女性時庫負責清理該執行緒相關資源 //detach()會讓子執行緒失去我們的控制,使用detach()后不能join() #include<iostream> #include<thread> using namespace std; void test() { cout << "我的執行緒執行" << endl; for (int i = 0; i < 10; ++i); cout << "我的執行緒執行完畢" << endl; } int main() { thread th(test); th.detach(); cout << "主執行緒執行完畢" << endl; return 0; }
主執行緒執行完畢后,子執行緒由運行時庫接管,無法列印出來
-
joinable()
//joinable():判斷是否可以成功join()或者detach(),回傳true或者false #include<iostream> #include<thread> using namespace std; void test() { cout << "我的執行緒執行" << endl; for (int i = 0; i < 10; ++i); cout << "我的執行緒執行完畢" << endl; } int main() { thread th(test); if (th.joinable()) { cout << "1:joinable()==true" << endl; } else { cout << "1:joinable()==false" << endl; } th.detach(); if (th.joinable()) { cout << "2:joinable()==true" << endl; } else { cout << "2:joinable()==false" << endl; } cout << "主執行緒執行完畢" << endl; return 0; }

-
-
其他創建執行緒的方法
-
類物件作為可呼叫物件
#include<iostream> #include<thread> using namespace std; class Test { public: void operator()() {//不能帶引數 cout << "我的執行緒開始執行" << endl; for (int i = 0; i < 10; ++i); cout << "我的執行緒執行完畢" << endl; } }; int main() { Test test; thread th(test); th.join(); cout << "主執行緒執行完畢" << endl; return 0; }若把join()改成detach(),主執行緒執行結束,test還在嗎?
如果物件不在了,執行緒還能繼續執行嗎?
這個物件實際上是被復制到執行緒中去,執行完執行緒后,test會被銷毀,但是復制的物件還在
#include<iostream> #include<thread> using namespace std; class Test { public: Test(){ cout << "Test()建構式被執行" << endl; } Test(const Test& test) { cout << "拷貝建構式執行" << endl; } ~Test() { cout << "解構式執行" << endl; } void operator()() {//不能帶引數 cout << "執行緒開始執行" << endl; for (int i = 0; i < 10; ++i); cout << "執行緒執行完畢" << endl; } }; int main() { Test test; thread th(test); th.detach(); for (int i = 0; i < 10; ++i) { cout << "主執行緒執行完畢" << i << endl; } return 0; }
將detach()改為join()后

-
用lambda運算式
#include<iostream> #include<thread> using namespace std; int main() { auto test = [] { cout << "執行緒開始執行" << endl; for (int i = 0; i < 10; ++i); cout << "執行緒執行完畢" << endl; }; thread th(test); th.join(); cout << "主執行緒執行完畢" << endl; return 0; }
-
-
傳遞臨時物件作為執行緒引數
-
可呼叫物件帶有引數時
#include<iostream> #include<thread> using namespace std; //傳入的不是myNum的參考,指向地址不同,實際是值傳遞,即便主執行緒detach(),子執行緒使用num不會出問題 //傳入的指標依舊是指向muBuf的地址,所以detach后,子執行緒會出問題,可以將char*改為const string& //使用const string&也有問題,即:不知道mybuf不知道什么時候轉換為string,如果主執行緒執行完畢后,還沒轉換完畢,就有問題 void test(const int& num, char* buf) { cout << "執行緒開始執行" << endl; cout << num << endl; cout << buf << endl; cout << "執行緒執行完畢" << endl; } int main() { int myNum = 1; int& num = myNum; char myBuf[] = "only for test!"; thread th(test, num, myBuf); th.join(); cout << "主執行緒執行完畢" << endl; return 0; }// 生成一個臨時string物件,系結const string& //使用這個方法,就會讓string物件在主執行緒執行完畢前構造 //在創建執行緒的同時構造臨時物件是可行的 thread th(test,num,string(myBuf));總結(針對detach())
(1)若傳遞int這種簡單型別引數,建議都是值傳遞,不要用參考
(2)如果傳遞類物件,避免隱式型別轉換,全部都要在創建執行緒就構建臨時物件,然后函式引數中用參考,否則系統還會構造一次
-
執行緒id的概念
(1)每個執行緒(包括主執行緒)實際都對應著一個數字,并且數字都不同
(2)執行緒id可以用c++標準庫中的函式來獲取,即:std::this_thread::get_id()
-
臨時物件構造時機抓捕
#include<iostream> #include<thread> using namespace std; class A { public: int num; //型別轉換建構式,可以把一個int轉換成一個類A物件 A(int num) :num(num) { cout << "建構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } A(const A& a) :num(a.num) { cout << "拷貝建構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } ~A() { cout << "解構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } }; void test(const A& a) { cout << "執行緒test的引數地址是" << &a << "threadid=" << std::this_thread::get_id() << endl; } int main() { cout << "主執行緒id是" << std::this_thread::get_id() << endl; int num = 2; thread th(test, num); th.join(); cout << "主執行緒執行完畢" << endl; return 0; }
A類物件在子執行緒中構造的,若改為detach()后,當主執行緒執行完畢后,還沒構造,就出現問題了
//在創建執行緒就構建臨時物件 thread th(test, A(num));

-
-
傳遞類物件、智能指標作為執行緒引數
-
執行緒引數為應用時修改值,不會影響主執行緒的值
#include<iostream> #include<thread> using namespace std; class A { public: mutable int num;//標注為const也能修改 //型別轉換建構式,可以把一個int轉換成一個類A物件 A(int num) :num(num) { cout << "建構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } A(const A& a) :num(a.num) { cout << "拷貝建構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } ~A() { cout << "解構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } }; void test(const A& a) { a.num = 199;//修改不會影響main的值 cout << "a.num=" << a.num << endl; cout << "執行緒test的引數地址是" << &a << "threadid=" << std::this_thread::get_id() << endl; } int main() { cout << "主執行緒id是" << std::this_thread::get_id() << endl; A a(10); thread th(test, a);//將類物件作為執行緒引數 th.join(); cout << "主執行緒的a.num=" << a.num << endl; cout << "主執行緒執行完畢" << endl; return 0; }

-
std::ref函式,可以讓執行緒傳入的引數不被復制(注:沒使用detach())
//將執行緒傳入引數加上std::ref thread th(test, std::ref(a));

-
傳遞智能指標
#include<iostream> #include<thread> using namespace std; void test(unique_ptr<int> uptr) { cout << "子執行緒id是" << std::this_thread::get_id() << endl; cout << "當前智能指標的地址是" << uptr << endl; } int main() { cout << "主執行緒id是" << std::this_thread::get_id() << endl; unique_ptr<int> uptr(new int(10)); cout << "當前智能指標的地址為" << uptr << endl; thread th(test, std::move(uptr));//將類物件作為執行緒引數 th.join(); cout << "主執行緒執行完畢" << endl; return 0; }

若改為detach(),當主執行緒執行完畢后,uptr被釋放,但子執行緒還在執行,此時就會出現問題
-
-
用成員函式指標做執行緒函式
#include<iostream> #include<thread> using namespace std; class A { public: mutable int num; //型別轉換建構式,可以把一個int轉換成一個類A物件 A(int num) :num(num) { cout << "建構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } A(const A& a) :num(a.num) { cout << "拷貝建構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } ~A() { cout << "解構式執行" << this << "threadid=" << std::this_thread::get_id() << endl; } void thread_work(int num) { cout << "子執行緒thread_work執行了" << ",threadid=" << std::this_thread::get_id() << endl; } }; void test(const A& a) { a.num = 199;//修改不會影響main的值 cout << "a.num=" << a.num << endl; cout << "執行緒test的引數地址是" << &a << "threadid=" << std::this_thread::get_id() << endl; } int main() { cout << "主執行緒id是" << std::this_thread::get_id() << endl; A a(10); //第一個引數是物件函式指標,第二個引數物件,其后引數為函式所需引數 thread th(&A::thread_work, a, 1); th.join(); cout << "主執行緒的a.num=" << a.num << endl; cout << "主執行緒執行完畢" << endl; return 0; }
c++11執行緒的互斥量
-
創建和等待多個執行緒
#include<iostream> #include<thread> #include<vector> using namespace std; //執行緒的入口函式 void test(int num) { cout << "執行緒開始執行,執行緒編號=" << num << endl; for (int i = 0; i < 10; ++i); cout << "執行緒執行結束了,執行緒編號=" << num << endl; } int main() { cout << "主執行緒開始執行" << endl; //創建和等待多個執行緒 vector<thread> threads; //創建10個執行緒,執行緒入口統一使用test for (int i = 0; i < 10; ++i) { threads.push_back(thread(test, i));//創建10個執行緒,同時這10個執行緒開始執行 } for (auto i = threads.begin(); i != threads.end(); ++i) { i->join();//等待10個執行緒回傳 } cout << "主執行緒執行結束" << endl; return 0; }
表明執行緒的執行是無序的,和作業系統內部對執行緒的調度有關
-
資料共享問題分析——mutex
-
只讀的資料
#include<iostream> #include<thread> #include<vector> using namespace std; int global = 10;//共享資料 //執行緒的入口函式 void test(int num) { cout << "編號為" << std::this_thread::get_id() << "列印global_v的值" << global << endl; for (int i = 0; i < 10; ++i); cout << "執行緒執行結束了,執行緒編號=" << std::this_thread::get_id() << endl; } int main() { cout << "主執行緒開始執行" << endl; //創建和等待多個執行緒 vector<thread> threads; //創建10個執行緒,執行緒入口統一使用test for (int i = 0; i < 10; ++i) { threads.push_back(thread(test, i));//創建10個執行緒,同時這10個執行緒開始執行 } for (auto i = threads.begin(); i != threads.end(); ++i) { i->join();//等待10個執行緒回傳 } cout << "主執行緒執行結束" << endl; return 0; }

所有執行緒讀取的資料都相等,即:只讀資料安全穩定
-
有讀有寫
#include<iostream> #include<thread> using namespace std; int num[10] = { 0 }; void write() { for (int i = 0; i < 10; ++i) { num[i] = 1; this_thread::sleep_for(std::chrono::milliseconds(1)); } } void read() { for (int i = 1; i < 10; ++i) { if (num[i] != num[i - 1]) { cout << "資料不一致" << endl; } } } int main() { cout << "主執行緒開始執行" << endl; //創建2個執行緒,一個執行緒負責讀,一個負責寫 thread write(write); thread read(read); write.join(); read.join(); cout << "主執行緒執行結束" << endl; return 0; }
-
-
共享資料的保護
-
互斥量的基本概念
是類的物件,可以理解為一把鎖,多個執行緒嘗試用lock()成員函式來加鎖這把鎖頭,只有一個執行緒可以鎖定成功(成功的標志是lock()函式回傳),如果沒有鎖定成功,執行緒就會阻塞在這里,不斷嘗試加鎖
-
互斥量的用法
-
先lock(),操作共享資料,unlock()
-
lock()和unlock()要成對使用,lock()必然要有unlock,每呼叫一次lock(),必然要呼叫一次unlock()
-
注意:mutex是不可復制物件,所以mutex作為類的成員要注意,C++中thread呼叫“帶mutex的類”的成員函式報錯C2661:std::tuple解決方法_tomwillow的博客-CSDN博客
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int num[10] = { 0 }; void write() { int copy[10]; for (int i = 0; i < 10; ++i) { copy[i] = 1; this_thread::sleep_for(std::chrono::milliseconds(1)); } g_mutex.lock(); memcpy(num, copy, 10); g_mutex.unlock(); } void read() { int copy[10] = { 0 }; g_mutex.lock(); memcpy(copy, num, 10); g_mutex.unlock(); for (int i = 1; i < 10; ++i) { if (copy[i] != copy[i - 1]) { cout << "資料不一致" << endl; } } } int main() { cout << "主執行緒開始執行" << endl; //創建2個執行緒,一個執行緒負責讀,一個負責寫 thread write(write); thread read(read); write.join(); read.join(); cout << "主執行緒執行結束" << endl; return 0; } -

-
為了防止忘記unlock(),引入一個叫std::lock_guard的類模板,類似于智能指標
(1)std::lock_guard直接取代了lock()和unlock()
(2)lock_guard建構式中執行了lock(),解構式中執行了unlock()
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int num[10] = { 0 }; void write() { int copy[10]; for (int i = 0; i < 10; ++i) { copy[i] = 1; this_thread::sleep_for(std::chrono::milliseconds(1)); } std::lock_guard<mutex> write_mutex(g_mutex); memcpy(num, copy, 10); } void read() { int copy[10] = { 0 }; std::lock_guard<mutex> read_mutex(g_mutex); memcpy(copy, num, 10); for (int i = 1; i < 10; ++i) { if (copy[i] != copy[i - 1]) { cout << "資料不一致" << endl; } } } int main() { cout << "主執行緒開始執行" << endl; //創建2個執行緒,一個執行緒負責讀,一個負責寫 thread write(write); thread read(read); write.join(); read.join(); cout << "主執行緒執行結束" << endl; return 0; }如果不想執行緒執行完畢后std::lock_guard才執行解構式,可以將其和需要加鎖的共享資料放到{}中
{ std::lock_guard<mutex> write_mutex(g_mutex); memcpy(num, copy, 10); } //此時std::lock_guard屬于{}這個作用域,當在{}外后,std::lock_guard生命周期結束,就會析構,即執行unlock -
死鎖
-
死鎖的前提是至少有兩個互斥量(鎖頭)才能產生
-
目前有兩個互斥量,即:鎖1,鎖2,兩個執行緒A,B
(1)執行緒A執行的時候,這個執行緒先鎖鎖1,把鎖1鎖成功了,然后它去鎖鎖2,此時出現背景關系切換,執行緒A讓出CPU
(2)執行緒B執行了,這個執行緒先鎖鎖2,因為鎖2沒有被鎖,所以鎖2鎖成功,然后它去鎖鎖1
(3)此時死鎖產生,執行緒A拿不到鎖2,解不開鎖1,執行緒B拿不到鎖1,解不開鎖2,兩個執行緒就無限互相等待下去
-
死鎖的一般解決辦法:保證兩個互斥量的lock()順序一致
-
std::lock()函式模板
-
一次鎖住兩個或兩個以上的互斥量(1個不行),它就不會存在多個執行緒因為鎖的順序導致的死鎖風險,
-
如果互斥量中有一個沒鎖住,它就會一直等待,直到所有的互斥量都鎖住
-
要么多個互斥量都鎖住,要么都沒有鎖住,如果只鎖一個其他沒成功,它就會立即把已經lock()的都unlock()
mutex g_mutex1; mutex g_mutex2; std::lock(g_mutex1,g_mutex2); g_mutex1.unlock(); g_mutex2.unlock(); -
std::lock_guard的std::adopt_lock
//是一個結構體物件,其一個標記作用,表示互斥量已經lock() //std::adopt_lock讓lock_guard不執行lock() mutex g_mutex1; mutex g_mutex2; std::lock(g_mutex1,g_mutex2); std::lock_guard<mutex> guard1(g_mutex1,std::adopt_lock); std::lock_guard<mutex> guard2(g_mutex2,std::adopt_lock);
-
-
unique_lock
(1)unique_lock是個類模板,和lock_guard類似,都是對mutex進行lock()和unlock()操作的
(2)unique_lock比lock_guard更靈活,但效率上差一點,記憶體占用多一點,
(3)用法和lock_guard類似
mutex g_mutex; std::unique_lock<std::mutex> uniqueLock(g_mutex);(4)unique_lock第二個引數
? a. std::adopt_lock(lock_guard也可使用):表示互斥量已經lock(),即:不需要在unique_lock的建構式進行lock()
? b. std::try_to_lock:會嘗試mutex的lock()去鎖定mutex,但如果沒有鎖定成功,就會立即回傳,并不會阻塞(前提是使用try_to_lock前不能單獨使用lock())
std::unique_lock<mutex> uniqueLock(g_mutex,std::try_to_lock); if(uniqueLock.owns_lock()){//嘗試Lock()成功 } //實體 #include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int num[10] = { 0 }; void write() { int copy[10]; for (int i = 0; i < 10; ++i) { copy[i] = 1; } unique_lock<mutex> uniqueLock(g_mutex); this_thread::sleep_for(chrono::milliseconds(2000)); memcpy(num, copy, 10); } void read() { int copy[10] = { 0 }; unique_lock<mutex> uniqueLock(g_mutex,std::try_to_lock); if (uniqueLock.owns_lock()) { memcpy(copy, num, 10); } else { cout << "執行緒2沒有拿到鎖" << endl; } for (int i = 1; i < 10; ++i) { if (copy[i] != copy[i - 1]) { cout << "資料不一致" << endl; } } } int main() { cout << "主執行緒開始執行" << endl; //創建2個執行緒,一個執行緒負責讀,一個負責寫 thread write(write); thread read(read); write.join(); read.join(); cout << "主執行緒執行結束" << endl; return 0; }
? c. std::defer_lock:并沒有給mutex,即:初始化一個沒有lock()的mutex(defer_lock的前提是不能先lock()),經常配合unique_lock的成員函式使用
-
unique_lock的重要成員函式
(1)lock()
(2)unlock():unique_lock也可以自動解鎖
(3)try_lock():(類似于try_to_lock)嘗試給互斥量加鎖,如果拿不到鎖,則回傳false,如果拿到了鎖,回傳true,這個函式是不阻塞的
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int num[10] = { 0 }; void write() { int copy[10]; for (int i = 0; i < 10; ++i) { copy[i] = 1; } unique_lock<mutex> uniqueLock(g_mutex); this_thread::sleep_for(chrono::milliseconds(2)); memcpy(num, copy, 10); } void read() { int copy[10] = { 0 }; unique_lock<mutex> uniqueLock(g_mutex,defer_lock); if (uniqueLock.try_lock() == true) { memcpy(copy, num, 10); } else { cout << "執行緒2沒有拿到鎖" << endl; } for (int i = 1; i < 10; ++i) { if (copy[i] != copy[i - 1]) { cout << "資料不一致" << endl; } } } int main() { cout << "主執行緒開始執行" << endl; //創建2個執行緒,一個執行緒負責讀,一個負責寫 thread write(write); thread read(read); write.join(); read.join(); cout << "主執行緒執行結束" << endl; return 0; }
(4)release():回傳它所管理的mutex物件指標,并釋放所有權,也就是說,這個unique_lock和mutex不再聯系(unlock不會釋放所有權),
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int num[10] = { 0 }; void write() { int copy[10]; for (int i = 0; i < 10; ++i) { copy[i] = 1; } unique_lock<mutex> uniqueLock(g_mutex); mutex* ptx = uniqueLock.release();//釋放mutex所有權,所以需要自己unlock() memcpy(num, copy, 10); ptx->unlock(); } void read() { int copy[10] = { 0 }; unique_lock<mutex> uniqueLock(g_mutex); memcpy(copy, num, 10); for (int i = 1; i < 10; ++i) { if (copy[i] != copy[i - 1]) { cout << "資料不一致" << endl; } } } int main() { cout << "主執行緒開始執行" << endl; //創建2個執行緒,一個執行緒負責讀,一個負責寫 thread write(write); thread read(read); write.join(); read.join(); cout << "主執行緒執行結束" << endl; return 0; } -
unique_lock所有權
mutex g_mutex; unique_lock<mutex> uniqueLock(g_mutex);//uniqueLock擁有g_mutex的所有權 //不能復制所有權,但是所有權可以轉移 //轉移方法1 unique_lock<mutex> uniqueLock_(move(uniqueLock));//當前uniqueLock失去g_mutex控制權,指向空,uniqueLock_指向g_mutex //轉移方法2 //從函式回傳一個區域的unique_lock物件是允許的 //回傳這種區域物件會導致系統生成臨時的unique_lock物件,并呼叫unique_lock的移動建構式 unique_lock<mutex> rtn_unique_lock(){ unique_lock<mutex> uniqueLock(g_mutex); return uniqueLock; }
-
-
-
單例設計模式共享資料分析、解決,call_once
-
單例設計模式
(1)單例:整個專案中,有某個或者某些特殊的類,并且只能創建一個屬于該類的物件
(2)單例類示例
#include<iostream> using namespace std; class MyCAS {//這是一個單例類 private: MyCAS(){}//私有化建構式 private: static MyCAS* m_instance;//靜態成員變數 public: static MyCAS* getInstance() { if (m_instance == nullptr) { m_instance = new MyCAS(); static Release release;//當程式退出時執行解構式 } return m_instance; } class Release {//用來釋放物件 public: ~Release() { if (MyCAS::m_instance!=nullptr) { delete m_instance; MyCAS::m_instance = nullptr; } } }; }; //類靜態變數初始化 MyCAS* MyCAS::m_instance = nullptr; int main() { MyCAS* ptr = MyCAS::getInstance();//創建一個物件,回傳該類(物件)指標 MyCAS* ptr_ = MyCAS::getInstance(); cout << "ptr=" << ptr << endl; cout << "ptr_=" << ptr_ << endl; return 0; }

-
單例設計模式共享資料問題分析、解決
問題:在創建的執行緒(非主執行緒)中創建單例類物件,并且這種執行緒可能不止一個,就需要將getInstance()這種成員函式互斥
原因:當第一個執行緒在new之前失去cpu,第二個執行緒就會執行new一次,此時第一個執行緒再次執行,就又會new一次,這就不符合單例設計模式了
示例:
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; class MyCAS {//這是一個單例類 private: MyCAS(){}//私有化建構式 private: static MyCAS* m_instance;//靜態成員變數 public: static MyCAS* getInstance() { //提高效率,只有第一次沒初始化時會加鎖 if (m_instance == nullptr) {//雙重鎖定(雙重檢查) unique_lock<mutex> lock(g_mutex);//自動加鎖 if (m_instance == nullptr) { m_instance = new MyCAS(); static Release release;//當程式退出時執行解構式 } } return m_instance; } class Release {//用來釋放物件 public: ~Release() { if (MyCAS::m_instance!=nullptr) { delete m_instance; MyCAS::m_instance = nullptr; } } }; void fun() { cout << "測驗" << endl; } }; //類靜態變數初始化 MyCAS* MyCAS::m_instance = nullptr; void thread1() { cout << "執行緒th1開始執行" << endl; MyCAS* ptr = MyCAS::getInstance(); cout << "執行緒th1執行完畢" << endl; } void thread2() { cout << "執行緒th2開始執行" << endl; MyCAS* ptr = MyCAS::getInstance(); cout << "執行緒th2執行完畢" << endl; } int main() { thread th1(thread1); thread th2(thread2); th1.join(); th2.join(); return 0; } -
std::call_once()——c++11標準
函式的第二個引數是一個函式名,需要和std::once_flag標記結合使用
功能:保證函式只被呼叫一次,即:通過std::once_flag這個標記來判斷函式是否執行,當呼叫call_once()成功后,call_once()就把這個標記設定為一種已呼叫狀態,后續再次呼叫call_once(),只要once_falg被設定為已呼叫,就不會執行了
具備互斥量的能力
#include<iostream> #include<thread> #include <mutex> using namespace std; once_flag g_flag;//這是系統定義的標記 class MyCAS {//這是一個單例類 static void CreateInstance() {//只被呼叫一次 m_instance = new MyCAS(); static Release release;//當程式退出時執行解構式 } private: MyCAS(){}//私有化建構式 private: static MyCAS* m_instance;//靜態成員變數 public: static MyCAS* getInstance() { //兩個執行緒同時執行到這里,其中一個執行緒要等另外一個執行緒執行完畢CreateInstance后,就會放棄執行CreateInstance call_once(g_flag, CreateInstance); return m_instance; } class Release {//用來釋放物件 public: ~Release() { if (MyCAS::m_instance!=nullptr) { delete m_instance; MyCAS::m_instance = nullptr; } } }; void fun() { cout << "測驗" << endl; } }; //類靜態變數初始化 MyCAS* MyCAS::m_instance = nullptr; void thread1() { cout << "執行緒th1開始執行" << endl; MyCAS* ptr = MyCAS::getInstance(); cout << "執行緒th1執行完畢" << endl; } void thread2() { cout << "執行緒th2開始執行" << endl; MyCAS* ptr = MyCAS::getInstance(); cout << "執行緒th2執行完畢" << endl; } int main() { thread th1(thread1); thread th2(thread2); th1.join(); th2.join(); return 0; }c++11執行緒的條件變數
-
-
輪詢機制:每隔一定時間,進行查詢
缺點:查詢不能太頻繁(浪費CPU),也不能太不頻繁(緩沖區滿),難以把握,性能不佳
#include <iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int g_buf[100];//緩沖區,最多存放一百個數 int g_count = 0; //第一個執行緒:生產者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一個1……20之間的亂數 this_thread::sleep_for(chrono::milliseconds(50 * r));//休息時間在50-1000毫秒之間 //存放一個物品(這里存放的資料代表物品) g_mutex.lock(); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_mutex.unlock(); } } //第二個執行緒:消費者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50)); g_mutex.lock(); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } g_mutex.unlock(); } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }
-
條件變數——std::condition_variable
(1)std::condition_variable實際是一個類,是一個和條件相關的一個類,即:等待一個條件達成,
(2)這個類是需要和互斥量來配合作業,用的時候我們要生成這個類的物件
(3)wait()
? a. 如果第二個引數(可呼叫物件)回傳的值是false,那么wait()將解鎖互斥量,并堵塞到 本行,堵塞到其他某個執行緒呼叫notify_one()成員函式為止,如果第二個引數回傳值是true,那么wait()會直接回傳
? b. 如果wait()沒有第二個引數,那么就和第二個引數回傳false效果一樣,
(4)notify_one()
? (1)嘗試把一個wait()的執行緒喚醒,如果沒有wait()執行緒,那么notify_one()就沒效果,但是不會阻塞在notify_one()這里
? (2)當其他執行緒用notify_one()將wait()【原本阻塞】,wait()就開始恢復干活了,即
? a. 不斷嘗試重新獲取互斥量鎖,如果獲取不到,執行緒就阻塞在wait()這里等著獲取鎖, b. 如果獲取到就繼續執行;獲取到鎖就執行lock(),
? 如果wait有第二個引數,就判斷這個引數回傳值,如果回傳值為false,wait()又會對互斥量解鎖,并再次阻塞,等待notify_one()喚醒
? 如果wait沒有第二個引數,則執行緒繼續執行
(5)示例
#include <iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; condition_variable g_cond;//生成一個條件變數物件 int g_buf[100];//緩沖區,最多存放一百個數 int g_count = 0; //第一個執行緒:生產者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一個1……20之間的亂數 this_thread::sleep_for(chrono::milliseconds(50 * r));//避免本執行緒notify_one()后比wait()先拿到鎖,休息時間在50-1000毫秒之間 //存放一個物品(這里存放的資料代表物品) unique_lock<mutex> lock(g_mutex); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_cond.notify_one(); } } //第二個執行緒:消費者 void Consume() { while (true) { unique_lock<mutex> lock(g_mutex); g_cond.wait(lock, [&] {//lambda為可呼叫物件 if (g_count > 0) return true; return false; }); for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; } -
上述代碼深入思考
(1) 當洗掉this_thread::sleep_for(chrono::milliseconds(50 * r))后,放入和消耗就可能不會交替執行,因為notify_one()后,執行緒可能先于wait()拿到鎖

(2)注意:notify_one()不一定起作用,但不會阻塞,因為執行notify_one()時,如果沒有其他執行緒wait(),它就沒有效果
-
notify_all()
當程式中有多個執行緒使用wait()時,使用notify_one()只會在某時刻喚醒其中任意一個執行緒,另外其他執行緒依然在阻塞中,即:任意時刻只有一個wait()嘗試拿鎖,其他都在阻塞中
使用notify_all()會將所有wait()的執行緒都喚醒,所有執行緒的wait()都會嘗試拿鎖
c++11 async、future、packaged_task、promise
-
std::asyc、std::future創建后臺任務并回傳值
-
std::asyc:是一個函式模板,用來啟動一個異步任務,啟動一個異步任務后,它會回傳一個std::future(類模板)物件
-
啟動一個異步任務:就是自動創建一個執行緒并開始執行對應的執行緒入口函式,它回傳一個std::future物件
-
std::future物件里就含有執行緒入口函式回傳的結果(執行緒回傳的結果),可以通過呼叫future物件的成員函式get()來獲取結果
-
std::future:提供了一種訪問異步操作結果的機制,即:這個結果可能無法馬上到達,但是不久的將來,當執行緒執行完畢的時候,就可以拿到結果
-
示例
#include<iostream> #include<future> using namespace std; int myThread() {//執行緒入口函式 cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; future<int> result = async(myThread);//創建一個執行緒并執行,但是主函式不會阻塞在這里,會繼續向下執行 cout << "continue……!" << endl; cout << result.get() << endl;//主函式會阻塞在這里,等待執行緒回傳 cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }#include<iostream> #include<future> using namespace std; class Thread { public: int myThread(int num) {//執行緒入口函式 cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } }; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; Thread th; future<int> result = async(&Thread::myThread,&th,12);//創建一個執行緒并執行,但是主函式不會阻塞在這里,會繼續向下執行 cout << "continue……!" << endl; cout << result.get() << endl;//主函式會阻塞在這里,等待執行緒回傳 cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; } -
上述程式通過future物件的get()成員函式等待執行緒結束并回傳結果,即:會阻塞future物件所在的執行緒,并回傳結果
-
future物件的get()成員函式只能呼叫一次
-
future物件還有一個wait()成員函式,該函式只是等待執行緒結束,本身不會回傳結果
-
額外向std::async()傳遞一個引數,該引數型別是std::launch型別(列舉型別),來達到一些特殊的目的
(1)std::launch::deferred:不會創建新執行緒,通過future物件呼叫get()或者wait()函式,就會直接呼叫async中的可呼叫物件
#include<iostream> #include<future> using namespace std; class Thread { public: int myThread(int num) {//執行緒入口函式 cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } }; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; Thread th; future<int> result = async(launch::deferred, &Thread::myThread, &th, 12); cout << "continue……!" << endl; cout << result.get() << endl; cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
(2)std::launch::async:強制這個異步任務在新執行緒上執行,即:系統必須創建出新執行緒執行可呼叫物件
(3)std::launch::async | std::launch::deferred:這是async的默認值,表示呼叫async的行為可能是創建新執行緒并立即執行,或者沒有創建新執行緒并延遲到呼叫get()或者wait()才開始執行任務入口函式
-
std::async和std::thread的區別:
(1)std::async不一定會創建新執行緒執行可呼叫物件,std::thread會創建新執行緒,如果系統資源緊張,創建執行緒失敗,那么整個程式就會崩潰
(2)std::async呼叫方法可以用簡單的get()函式拿到執行緒可呼叫物件的回傳值
(3)std::thread創建的執行緒過多,可能創建失敗,系統報告例外,崩潰,而std::async一般不會報告例外、崩潰,因為當系統資源緊張導致無法創建新執行緒的時候,std::async使用默認值呼叫時就不會創建新執行緒,而是后序當某個執行緒使用get()獲取回傳值時,就在該執行緒執行可呼叫物件
-
std::async使用默認值
當std::async使用默認值作為引數時,會產生不確定性【即:是否創建新執行緒】
借助future的wait_for()來判斷是否創建新執行緒
#include<iostream> #include<thread> #include<atomic> #include<future> using namespace std; atomic<int> g_num = 0; int myThread() { cout << "myThread() start, " << "thread_id=" << this_thread::get_id() << endl; cout << "myThread() end, " << "thread_id=" << this_thread::get_id() << endl; return 1; } int main() { cout << "main() start, " << "thread_id=" << this_thread::get_id() << endl; future<int> result = async(myThread); future_status status = result.wait_for(0s);//等待chrono::seconds(0) if (status == future_status::deferred) { cout << "沒有創建新執行緒" << endl; cout << result.get() << endl;//此時才執行myThread() } else { //創建了新執行緒 if (status == future_status::ready) { cout << "執行緒成功創建" << endl; cout << result.get() << endl; } else if (status == future_status::timeout) { //超時,執行緒還沒執行完畢 cout << "超時,執行緒還在執行中" << endl; cout << result.get() << endl; } } cout << "main() end, " << "thread_id=" << this_thread::get_id() << endl; return 0; }
-
-
std::packaged_task
-
std::packaged_task:是一個類模板,模板引數是各種可呼叫物件,通過std::packaged_task把各種可呼叫物件包裝起來,方便將來作為執行緒入口函式
-
packaged_task:包裝的物件還是可以直接呼叫的
-
可以通過get_future()獲取future物件,從而取得執行緒的回傳值
-
示例
#include<iostream> #include <future> #include<thread> using namespace std; int myThread(int num) {//執行緒入口函式 cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(int)> myFunc(myThread);//將函式myThread通過packaged_task包裝起來 thread th(std::ref(myFunc), 1); th.join(); std::future<int> result = myFunc.get_future(); cout << result.get() << endl; cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }#include<iostream> #include <future> #include<thread> using namespace std; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(int)> myFunc([](int num) { cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; }); thread th(ref(myFunc), 12); th.join(); cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }#include<iostream> #include <future> #include<thread> using namespace std; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(int)> myFunc([](int num) { cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; }); myFunc(12);//直接呼叫 future<int> result = myFunc.get_future(); cout << result.get() << endl;//注意,沒有創建新執行緒,而是觸發lambda運算式執行,即:相當于函式呼叫 cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }#include<iostream> #include <future> #include<thread> #include<vector> using namespace std; vector<packaged_task<int(int)>> vec; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(int)> myFunc([](int num) { cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; }); vec.push_back(move(myFunc));//這里用到了移動語意,此時myFunc為空 packaged_task<int(int)> myFunc_ = move(vec.back()); vec.pop_back(); myFunc_(123); future<int> result = myFunc_.get_future(); cout << result.get() << endl; cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
-
-
std::promise
std::promise也是一個類模板,能夠在某個執行緒中給它賦值,然后可以在其他執行緒中,將這個值取出使用
#include<iostream> #include<thread> #include <future> using namespace std; void myThread(promise<int>& temp, int num) { //模擬完成一系列復雜操作 this_thread::sleep_for(chrono::milliseconds(5000)); temp.set_value(num);//將結果保存到promise物件中 } int main() { promise<int> myTemp;//宣告一個promise物件,保存型別為int thread th(myThread, ref(myTemp), 520); th.join(); //獲取結果值 future<int> result = myTemp.get_future();//promise和future系結,用于獲取執行緒回傳值 cout << result.get() << endl; return 0; }#include<iostream> #include<thread> #include <future> using namespace std; void myThread(promise<int>& temp, int num) { //模擬完成一系列復雜操作 this_thread::sleep_for(chrono::milliseconds(5000)); temp.set_value(num);//將結果保存到promise物件中 } void myThread_(future<int>& temp) { int result = temp.get(); cout << "myThread_ result=" << result << endl; } int main() { promise<int> myTemp;//宣告一個promise物件,保存型別為int thread th(myThread, ref(myTemp), 520); th.join(); future<int> result = myTemp.get_future();//promise和future系結,用于獲取執行緒回傳值 thread th_(myThread_, ref(result)); th_.join(); return 0; }
future其他成員函式、shared_future、atomic
-
std::future其它成員函式
wait_for(time):阻塞time時長,若time時長后,執行緒還沒回傳,則wait_for回傳future_status::timeout,如果在time時長之內,執行緒成功回傳,則wait_for()回傳future_status::ready,如果async第一個引數設定為std::launch::deferred,則wait_for()回傳future_status::deferred
#include<iostream> #include<future> using namespace std; int myThread() {//執行緒入口函式 cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(1000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; future<int> result = async(myThread);//創建一個執行緒并執行,但是主函式不會阻塞在這里,會繼續向下執行 cout << "continue……!" << endl; //列舉型別 future_status status = result.wait_for(chrono::seconds(2)); if (status == future_status::timeout) { cout << "timeout" << endl; } else if (status == future_status::ready) { cout << "成功回傳" << endl; cout << result.get() << endl; } else if (status == future_status::deferred) { cout << "deferred" << endl; cout << result.get() << endl; } cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }

-
std::shared_future
std::future只能呼叫一次get()函式,因為get()函式的設計是一個移動語意
std::shared_future:也是一個類模板,get()函式是復制資料
#include<iostream> #include <future> #include<thread> using namespace std; int myThread() {//執行緒入口函式 cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } void myThread_(shared_future<int>& temp) { cout << "myThread_() start," << "threadid=" << this_thread::get_id() << endl; cout << "myThread_():" << temp.get() << endl; cout << "myThread_() end," << "threadid=" << this_thread::get_id() << endl; } int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(void)> myFunc(myThread);//將函式myThread通過packaged_task包裝起來 thread th(std::ref(myFunc)); th.join(); shared_future<int> result = myFunc.get_future(); cout << "main():"<< result.get() << endl; thread th_(myThread_, std::ref(result)); th_.join(); cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
-
std::atomic——原子操作
原子操作概念及其范例
(1)原子操作:是指“不可分割的操作”,要么執行成功,要么失敗,即:是在多執行緒中不會打斷的程式執行片段
(2)原子操作:比互斥量的效率更勝一籌
(3)互斥量加鎖一般針對的是一個代碼片段,而原子操作針對的一般都是一個變數,而不是一個代碼片段
(4)std::atomic:類模板,用來代表原子操作
(5)示例
#include<iostream> #include<thread> #include<atomic> using namespace std; atomic<int> g_num = 0; void Write() { for (int i = 0; i < 1000000; ++i) { //可替換成g_num+=1; ++g_num;//操作為原子操作,在多執行緒中不會被打斷 } } int main() { thread th1(Write); thread th2(Write); th1.join(); th2.join(); cout<<"兩個執行緒執行完畢,最終g_num="<<g_num<<endl; return 0; }#include<iostream> #include<thread> #include<atomic> using namespace std; atomic<bool> g_flag = false;//執行緒退出標記 void myThread() { cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; while (!g_flag) { cout << "myThread() run," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(1000)); } cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; } int main() { thread th1(myThread); thread th2(myThread); this_thread::sleep_for(chrono::milliseconds(2000)); g_flag = true; th1.join(); th2.join(); return 0; }(6)atomic原子操作,針對++,--,+=,-=,*=,&=,!=是支持的,其它可能不支持
#include<iostream> #include<thread> #include<atomic> using namespace std; atomic<int> g_num = 0; void Write() { for (int i = 0; i < 1000000; ++i) { g_num = g_num + 1; } } int main() { thread th1(Write); thread th2(Write); th1.join(); th2.join(); cout << "兩個執行緒執行完畢,最終g_num=" << g_num << endl; return 0; }
(7)load()函式:以原子方式讀取atomic物件的值
atomic<int> atm1; atomic<int> atm2=atm1;//嘗試參考已洗掉的函式,拷貝賦值運算子也不讓用 atomic<int> atm3(atm1.load())(8)store()函式:以原子方式寫入內容
atomic<int> atm; atm.store(1);
windows臨界區、其他各種mutex互斥量
-
windows臨界區
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; #define WINDOWS #ifdef WINDOWS CRITICAL_SECTION winsec;//windows中的臨界區,類似于mutex #endif mutex g_mutex; int g_buf[100];//緩沖區,最多存放一百個數 int g_count = 0; //第一個執行緒:生產者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一個1……20之間的亂數 this_thread::sleep_for(chrono::milliseconds(50 * r));//休息時間在50-1000毫秒之間 //存放一個物品(這里存放的資料代表物品) #ifdef WINDOWS EnterCriticalSection(&winsec);//進入臨界區,相當于mutex.lock() g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; LeaveCriticalSection(&winsec);//退出臨界區,相當于mutex.unlock() #else g_mutex.lock(); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_mutex.unlock(); #endif // WINDOWS } } //第二個執行緒:消費者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//輪詢 #ifdef WINDOWS EnterCriticalSection(&winsec); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } LeaveCriticalSection(&winsec); #else g_mutex.lock(); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } g_mutex.unlock(); #endif // WINDOWS } } int main() { #ifdef WINDOWS InitializeCriticalSection(&winsec);//使用臨界區之前初始化 #endif // WINDOWS srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; } -
多次進入臨界區試驗
(1)在同一個執行緒中,windows中相同臨界區變數代表的臨界區的進入(EnterCriticalSection)可以被多次呼叫,但是EnterCriticalSection的呼叫次數需要和LeaveCriticalSection的呼叫次數要相等(否則,該執行緒始終都在臨界區中,該執行緒一直執行,另一個執行緒阻塞)
(2)c++11中mutex不允許lock多次
-
自動析構技術
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; #define WINDOWS //用于自動釋放windows下的臨界區,防止忘記LeaveCriticalSection //類似與std::lock_guard //RAII類(Resource Acquisition initialization):資源獲取即初始化 class cWinLock { public: cWinLock(CRITICAL_SECTION* ptr) :ptr(ptr) { EnterCriticalSection(ptr); } ~cWinLock() { LeaveCriticalSection(ptr); } private: CRITICAL_SECTION* ptr; }; #ifdef WINDOWS CRITICAL_SECTION winsec;//windows中的臨界區,類似于mutex #endif mutex g_mutex; int g_buf[100];//緩沖區,最多存放一百個數 int g_count = 0; //第一個執行緒:生產者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一個1……20之間的亂數 this_thread::sleep_for(chrono::milliseconds(50 * r));//休息時間在50-1000毫秒之間 //存放一個物品(這里存放的資料代表物品) #ifdef WINDOWS cWinLock wLock(&winsec); cWinLock wLock_(&winsec); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; #else lock_guard<mutex> lock(g_mutex); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; #endif // WINDOWS } } //第二個執行緒:消費者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//輪詢 #ifdef WINDOWS cWinLock wLock(&winsec); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } #else lock_guard<mutex> lock(g_mutex); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } #endif // WINDOWS } } int main() { #ifdef WINDOWS InitializeCriticalSection(&winsec);//使用臨界區之前初始化 #endif // WINDOWS srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; } -
recursive_mutex遞回的獨占互斥量
std::mutex:獨占互斥量,在本執行緒lock后,本執行緒無法繼續lock【除非unlock后】,其它執行緒也無法lock
std::recursive_mutex:遞回的獨占互斥量,允許同一個執行緒中同一個互斥量多次被lock【第一次lock后沒有unlock】,效率上比mutex低
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; recursive_mutex g_mutex; int g_buf[100];//緩沖區,最多存放一百個數 int g_count = 0; //第一個執行緒:生產者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一個1……20之間的亂數 this_thread::sleep_for(chrono::milliseconds(50 * r));//休息時間在50-1000毫秒之間 //存放一個物品(這里存放的資料代表物品) lock_guard<recursive_mutex> lock(g_mutex); lock_guard<recursive_mutex> lock_(g_mutex); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; } } //第二個執行緒:消費者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//輪詢 lock_guard<recursive_mutex> lock(g_mutex); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; } -
帶超時的互斥量std::timed_mutex和std::recursive_timed_mutex
-
std::timed_mutex:帶超時功能的獨占互斥量
(1)try_lock_for():引數是一段時間,等待一段時間,如果等待超時或者lock成功,繼續流程
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; timed_mutex g_mutex;//帶超時功能的獨占互斥量 int g_buf[100];//緩沖區,最多存放一百個數 int g_count = 0; //第一個執行緒:生產者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一個1……20之間的亂數 //存放一個物品(這里存放的資料代表物品) if (g_mutex.try_lock_for(10ms)) {//等待100毫秒嘗試lock //在100毫秒內lock成功 g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_mutex.unlock(); } else { //沒有在100毫秒內lock成功 cout << "Producer lock失敗" << endl; this_thread::sleep_for(chrono::microseconds(100)); } } } //第二個執行緒:消費者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//輪詢 lock_guard<timed_mutex> lock(g_mutex); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }

(2)try_lock_until:引數是一個未來時間點,在這個未來時間沒到的時間內,不管lock是否成功,流程繼續
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; timed_mutex g_mutex;//帶超時功能的獨占互斥量 int g_buf[100];//緩沖區,最多存放一百個數 int g_count = 0; //第一個執行緒:生產者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一個1……20之間的亂數 //存放一個物品(這里存放的資料代表物品) if (g_mutex.try_lock_until(chrono::steady_clock::now() + 100ms)) {//當前時間加上100ms //在以當前時刻算起的100ms內,lock成功 g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_mutex.unlock(); } else { cout << "Producer lock失敗" << endl; this_thread::sleep_for(chrono::microseconds(100)); } } } //第二個執行緒:消費者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//輪詢 lock_guard<timed_mutex> lock(g_mutex); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }- std::recursive_timed:帶超時功能的遞回獨占互斥量
-
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/195889.html
標籤:C++
