C++11多線程互斥鎖mutex
,unique_lock
,lock_guard
互斥鎖
??互斥鎖是線程中常用的線程同步手段, 在C++11后使用互斥互斥鎖的方式包括兩種pthread_mutex_t
和std::mutex
pthread_mutex_t
這是Linux下pthread的鎖, 介紹
std::mutex
我們這里介紹一下C++11中
std::mutex
的基本使用
頭文件: #include<mutex>
成員函數(shù):
- 構(gòu)造函數(shù),
std::mutex _mutex;
不必傳入?yún)?shù), 不允許拷貝構(gòu)造和move
構(gòu)造 -
lock()
: 上鎖, 如果其他線程已經(jīng)持有鎖的話會一直阻塞 -
unlock()
: 解鎖 -
try_lock()
: 與lock
相同用于加鎖, 如果其他線程持有鎖的話立刻返回false
擴(kuò)展
??事實上還存在有其他類型的鎖, 比如:recursive_mutex
和time_mutex
這些鎖個人基本沒用到過, 可以實現(xiàn)遞歸加鎖解鎖/加鎖超時的限制, 如果有需要自行了解
進(jìn)階版使用, unique_lock
,lock_guard
對于以上的簡單使用其實與C語言相差不大, 但是我們可以使用
RAII
(通過類的構(gòu)造析構(gòu))來實現(xiàn)更好的編碼方式.
ps: C++相較于C引入了很多新的特性, 比如可以在代碼中拋出異常, 如果還是按照以前的加鎖解鎖的話代碼會極為復(fù)雜繁瑣
代碼:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::lock_guard
#include <stdexcept> // std::logic_error
std::mutex mtx;
void print_even (int x) {
if (x%2==0) std::cout << x << " is even\n";
else throw (std::logic_error("not even"));
}
void print_thread_id (int id) {
try {
// using a local lock_guard to lock mtx guarantees unlocking on destruction / exception:
std::lock_guard<std::mutex> lck (mtx);
print_even(id);
}
catch (std::logic_error&) {
std::cout << "[exception caught]\n";
}
}
int main ()
{
std::thread threads[10];
// spawn 10 threads:
for (int i=0; i<10; ++i)
threads[i] = std::thread(print_thread_id,i+1);
for (auto& th : threads) th.join();
return 0;
}
這里的lock_guard
換成unique_lock
也是一樣的, 我并未深入研究內(nèi)部實現(xiàn); 但是可以很簡單的猜到, 在構(gòu)造函數(shù)中加鎖,析構(gòu)函數(shù)中解鎖
unique_lock
,lock_guard
的區(qū)別
?? unique_lock
與lock_guard
都能實現(xiàn)自動加鎖和解鎖艺骂,但是前者更加靈活,能實現(xiàn)更多的功能建芙。unique_lock
可以進(jìn)行臨時解鎖和再上鎖棋返,如在構(gòu)造對象之后使用lck.unlock()
就可以進(jìn)行解鎖泳唠,lck.lock()
進(jìn)行上鎖,而不必等到析構(gòu)時自動解鎖乙埃。
unique_lock
擴(kuò)展條件變量
C++11提供
std::condition_variable
可以和std::mutex
配合使用, 不過往往是配合unique_lock
進(jìn)行使用, 所以在這里介紹一下
#include <iostream>
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
std::deque<int> q;
std::mutex mu;
std::condition_variable cond;
void fun1() {
while (true) {
std::unique_lock<std::mutex> locker(mu);
q.push_front(count);
locker.unlock();
cond.notify_one();
sleep(10);
}
}
void fun2() {
while (true) {
std::unique_lock<std::mutex> locker(mu);
cond.wait(locker, [](){return !q.empty();});
data = q.back();
q.pop_back();
locker.unlock();
std::cout << "thread2 get value form thread1: " << data << std::endl;
}
}
int main() {
std::thread t1(fun1);
std::thread t2(fun2);
t1.join();
t2.join();
return 0;
}
??條件變量的目的就是為了, 在沒有獲得某種提醒時長時間休眠; 如果正常情況下, 我們需要一直循環(huán)(+sleep), 這樣的問題就是CPU消耗
+時延問題
, 條件變量的意思是在cond.wait
這里一直休眠直到cond.notify_one
喚醒才開始執(zhí)行下一句; 還有cond.notify_all()
接口用于喚醒所有等待的線程.
cond.wait(locker, {return !q.empty();});: 條件變量可能會被意外喚醒, 可以額外傳入一個函數(shù)只有被喚醒時同時函數(shù)返回值為true
才會被真正喚醒(也可以不傳再外部判斷)
那么為什么必須使用unique_lock
呢?
原因: 條件變量在
wait
時會進(jìn)行unlock
再進(jìn)入休眠,lock_guard
并無該操作接口
wait: 如果線程被喚醒或者超時那么會先進(jìn)行lock
獲取鎖, 再判斷條件(傳入的參數(shù))是否成立, 如果成立則waut
函數(shù)返回否則釋放鎖繼續(xù)休眠
notify: 進(jìn)行notify
動作并不需要獲取鎖