C++11 thread 及互斥鎖、條件變數
阿新 • • 發佈:2019-02-04
啟動的格式:
#include <thread>//包含標頭檔案
void fucntion();//定義一個執行緒執行的函式
thread t1(fucntion);//執行緒啟動這個函式
//一建立就自動執行,主執行緒非阻塞
void factorial(int n,char b,..);//帶引數的函式,引數的個數順延
thread t2(factorial,5,'a',..);//執行緒啟動帶引數的函式
t1.join(); //阻塞
執行緒一些控制函式:
Join() :讓主執行緒等待 阻塞
t1.join();
Detach():非阻塞(預設)
t1.detach();//最好寫上
Move() :轉移、竊取
thread t2 = move(t1);
//執行緒無法賦值,只能被轉移相當於換個名字,但轉移後的t1無值
Ref() :執行緒傳遞引數傳遞的是引用,n為引數(需要互鎖,存在資料競爭)
thread t1(fucntion,ref(n));
使用互斥同步執行緒:
假如多個執行緒同時爭奪cout資源,則結果輸出無序,這時就需要使用互斥物件mutex。注意可能會出現死鎖。
#include <mutex>//包含標頭檔案
mutex mut;//定義一個互斥變數
mut.lock();//上鎖
cout << "我是通過主執行緒執行的" << i << endl;
mut.unlock();//解鎖
mutex的封裝 Lock_guard unique_lock:
lock_guard
把鎖放到lock_guard中時,mutex自動上鎖,lock_guard析構時,同時把mutex解鎖
void share_print(string msg, int id)
{
lock_guard<mutex> guard(mut);
cout << msg << " id = " << id << endl;
}
unique_lock
第三種方式加鎖:unique_lock 更有彈性
缺點:消耗更多系統資源
1 unique_lock<mutex> locker(m_mutex);//自動上鎖
cout << str << i << endl;
//解鎖
locker.unlock();
//。。。進行其他操作 更彈性
2 defer_lock表示不對mutex加鎖,只管理mutex,此時mutex是沒有加鎖的
unique_lock<mutex> locker(m_mutex,defer_lock);
//本區間未上鎖
locker.lock();
cout << str << i << endl;//上鎖
//解鎖
locker.unlock();
//。。。進行其他操作 更彈性
3 lock_gard 無法複製(=) 無法移動
unique_lock 可以被移動 mutex會被轉移,原先的失效
unique_lock<mutex> lokcer = move(locker1);
缺點:消耗更多效能 unique_lock更有彈性,lock_gard效能更優 unique_lock內部持有mutex的狀態:locked,unlocked。unique_lock比lock_guard佔用空間和速度慢一些,因為其要維護mutex的狀態。
死鎖:
什麼叫死鎖?
執行緒A和B互相等待各自持有的資源釋放,進入死迴圈。
死鎖產生
例如:
//一個執行緒中
lock_guard<mutex> guard1(m_mutex2);//執行成功 mutex2成功上鎖
lock_guard<mutex> guard(m_mutex1);//等待mutex1解鎖
//另一個執行緒
lock_guard<mutex> guard1(m_mutex1);//執行成功 mutex1成功上鎖
lock_guard<mutex> guard(m_mutex2);//等待mutex2解鎖
//則有可能死鎖
解決方案
1:只使用一個mutex
2:保證 locker順序相同
//一個執行緒中(先執行)
lock_guard<mutex> guard1(m_mutex1);//執行成功 mutex1成功上鎖
lock_guard<mutex> guard(m_mutex2);//等待mutex1解鎖
//另一個執行緒
lock_guard<mutex> guard1(m_mutex1);//等待mutex1解鎖
lock_guard<mutex> guard(m_mutex2);//等待mutex2解鎖
3:使用lock()
std::lock會使用一種避免死鎖的演算法對多個待加鎖物件進行lock操作,當待加鎖的物件中有不可用物件時std::lock會阻塞當前執行緒知道所有物件都可用。
lock(m_mutex1, m_mutex2);
lock_guard<mutex> guard1(m_mutex2, adopt_lock);
lock_guard<mutex> guard(m_mutex, adopt_lock);
//adopt_lock相當於讓lock()接手控制
4:清楚你寫的程式碼,避免產生此問題
條件變數
std::condition_variable
當std::condition_variable 物件的某個 wait 函式被呼叫的時候,它使用 std::unique_lock(通過 std::mutex) 來鎖住當前執行緒。當前執行緒會一直被阻塞,直到另外一個執行緒在相同的 std::condition_variable
物件上呼叫了notification 函式來喚醒當前執行緒。
notify_one() 喚醒一個在等待執行緒
notify_all()喚醒所有等待的執行緒
condition_variable cond;
cond.wait(locker);
cond.notify_one();