C++11併發學習之三:執行緒同步
1.<mutex> 標頭檔案介紹
(1)Mutex系列類(四種)
std::mutex,最基本的 Mutex 類。
std::recursive_mutex,遞迴 Mutex 類。
std::time_mutex,定時 Mutex 類。
std::recursive_timed_mutex,定時遞迴 Mutex 類。
(2)Lock系列類(兩種)
std::lock_guard,與 Mutex RAII 相關,方便執行緒對互斥量上鎖。
std::unique_lock,與 Mutex RAII 相關,方便執行緒對互斥量上鎖,但提供了更好的上鎖和解鎖控制。
(3)其他型別(結構體)
std::adopt_lock_t——它的常量物件定義為constexpr adopt_lock_t adopt_lock {};//constexpr 是C++11 中的新關鍵字)
std::defer_lock_t——它的常量物件定義為constexpr defer_lock_t defer_lock {}; //constexpr 是C++11 中的新關鍵字)
std::try_to_lock_t——它的常量物件定義為constexpr try_to_lock_t try_to_lock {};//constexpr 是C++11 中的新關鍵字)
(4)函式
std::try_lock,嘗試同時對多個互斥量上鎖。
std::lock,可以同時對多個互斥量上鎖。
std::call_once,如果多個執行緒需要同時呼叫某個函式,call_once 可以保證多個執行緒對該函式只調用一次。
2.常用型別舉例
(1)std::mutex類
☆建構函式,std::mutex不允許拷貝構造,也不允許 move 拷貝,最初產生的 mutex 物件是處於 unlocked 狀態的。
☆lock(),呼叫執行緒將鎖住該互斥量。執行緒呼叫該函式會發生下面 3 種情況:①如果該互斥量當前沒有被鎖住,則呼叫執行緒將該互斥量鎖住,直到呼叫 unlock之前,該執行緒一直擁有該鎖。②如果當前互斥量被其他執行緒鎖住,則當前的呼叫執行緒被阻塞住。③如果當前互斥量被當前呼叫執行緒鎖住,則會產生死鎖(deadlock)。
☆unlock(), 解鎖,釋放對互斥量的所有權。
☆try_lock(),嘗試鎖住互斥量,如果互斥量被其他執行緒佔有,則當前執行緒也不會被阻塞。執行緒呼叫該函式也會出現下面 3 種情況:① 如果該互斥量當前沒有被鎖住,則該執行緒鎖住互斥量,直到該執行緒呼叫 unlock 釋放互斥量。②如果當前互斥量被其他執行緒鎖住,則當前呼叫執行緒返回 false,而並不會被阻塞掉。③如果當前互斥量被當前呼叫執行緒鎖住,則會產生死鎖(deadlock)。
不論是lock()還是try_lock()都需要和unlock()配套使用,下面舉例說明lock()和try_lock()的區別。
#include <thread>
#include <iostream>
#include <string>
#include <chrono>
#include <assert.h>
#include <mutex>
int counter=0;
std::mutex mtx;
void func()
{
for (int i=0; i<10000; ++i)
{
mtx.lock();
++counter;
mtx.unlock();
}
}
int main()
{
std::thread workerThreads[10];
for (int i=0; i<10; ++i)
{
workerThreads[i] = std::thread(func);
}
for (auto& workerThread : workerThreads)
{
workerThread.join();
}
std::cout << counter << " successful increases of the counter"<<std::endl;
return 0;
}
由於lock()的阻塞特性,所以每個執行緒都統計了10000次,一共是10*10000=100000次。
#include <thread>
#include <iostream>
#include <string>
#include <chrono>
#include <assert.h>
#include <mutex>
int counter=0;
std::mutex mtx;
void func()
{
for (int i=0; i<10000; ++i)
{
if (mtx.try_lock())
{
++counter;
mtx.unlock();
}
}
}
int main()
{
std::thread workerThreads[10];
for (int i=0; i<10; ++i)
{
workerThreads[i] = std::thread(func);
}
for (auto& workerThread : workerThreads)
{
workerThread.join();
}
std::cout << counter << " successful increases of the counter"<<std::endl;
return 0;
}
由於try_lock()的非阻塞特性,如果當前互斥量被其他執行緒鎖住,則當前try_lock()返回 false,此時counter並不會增加1。所以這十個執行緒的統計結果具有隨機性,下次執行程式時,統計值不一定是16191。
(2).std::lock_guard和std::unique_lock類
std::lock_guard使用起來比較簡單,除了建構函式外沒有其他成員函式。
std::unique_lock除了lock_guard的功能外,提供了更多的成員函式,相對來說更靈活一些。這些成員函式包括lock,try_lock,try_lock_for,try_lock_until、unlock等。
std::unique_lock::lock——用它所管理的Mutex物件的 lock 函式。
std::unique_lock::try_lock——用它所管理的Mutex物件的 try_lock函式。
std::unique_lock::unlock——用它所管理的Mutex物件的 unlock函式。
這兩個類相比使用std::mutex的優勢在於不用配對使用,無需擔心忘記呼叫unlock而導致的程式死鎖。
#include <thread>
#include <iostream>
#include <string>
#include <chrono>
#include <assert.h>
#include <mutex>
int counter=0;
std::mutex mtx;
void func()
{
for (int i=0; i<10000; ++i)
{
//將std::lock_guard替換成std::unique_lock,效果是一樣的
std::lock_guard<std::mutex> lck (mtx);
++counter;
}
}
int main()
{
std::thread workerThreads[10];
for (int i=0; i<10; ++i)
{
workerThreads[i] = std::thread(func);
}
for (auto& workerThread : workerThreads)
{
workerThread.join();
}
std::cout << counter << " successful increases of the counter"<<std::endl;
return 0;
}
std::uniqure_lock建構函式的第二個引數可以是std::defer_lock,std::try_to_lock或std::adopt_lock
#include <thread>
#include <iostream>
#include <string>
#include <chrono>
#include <assert.h>
#include <mutex>
int counter=0;
std::mutex mtx;
void func()
{
for (int i=0; i<10000; ++i)
{
mtx.lock();
//注意此時Tag引數為std::adopt_lock表明當前執行緒已經獲得了鎖,
//此後mtx物件的解鎖操作交由unique_lock物件lck來管理,在lck的生命週期結束之後,
//mtx物件會自動解鎖。
std::unique_lock<std::mutex> lck(mtx,std::adopt_lock);
++counter;
}
}
int main()
{
std::thread workerThreads[10];
for (int i=0; i<10; ++i)
{
workerThreads[i] = std::thread(func);
}
for (auto& workerThread : workerThreads)
{
workerThread.join();
}
std::cout << counter << " successful increases of the counter"<<std::endl;
return 0;
}
#include <chrono>
#include <assert.h>
#include <mutex>
int counter=0;
std::mutex mtx;
void func()
{
for (int i=0; i<10000; ++i)
{
//注意此時Tag引數為std::defer_lock表明當前執行緒沒有獲得了鎖,
//需要通過lck的lock和unlock來加鎖和解鎖,
std::unique_lock<std::mutex> lck(mtx,std::defer_lock);
lck.lock();
++counter;
lck.unlock();
}
}
int main()
{
std::thread workerThreads[10];
for (int i=0; i<10; ++i)
{
workerThreads[i] = std::thread(func);
}
for (auto& workerThread : workerThreads)
{
workerThread.join();
}
std::cout << counter << " successful increases of the counter"<<std::endl;
return 0;
}
參考連結:http://www.cnblogs.com/haippy/p/3346477.html
---------------------
作者:燦哥哥
來源:CSDN
原文:https://blog.csdn.net/caoshangpa/article/details/52842618?utm_source=copy
版權宣告:本文為博主原創文章,轉載請附上博文連結!