c++ 學習之 多線程(六)lock_guard 和 unique_lock

c++ 學習之 多線程(六)lock_guard 和 unique_lock

前言

在使用mutex互斥量時,總會出現lock後沒有unlock的情況,尤其是在判斷分支中,某些被不常進入的分支忘記unlock,我們可以用RAII機制的模板類來解決忘記unlock的問題。

正文

1.std::lock_guard

std::lock_gurad 是 C++11 中定義的模板類,定義如下:
template <class Mutex> class lock_guard;

#include<stdio.h>
#include<thread>
#include<mutex>
using namespace std;
mutex m;
void fun(int& a)
{
	 for (int i = 0; i < 100000; i++)
	 {
	  lock_guard<mutex>m_guard(m);
	  a++;
	 }
}
int main()
{
         int a = 0;
	 thread t1(fun, ref(a));
	 thread t2(fun, ref(a));
	 t1.join();
	 t2.join();
	 printf("%d\n", a);
}

這種方法創建出來的lock_guard接管的mutex對象應該是未被加鎖的,創建時調會在構造函數中對mutex對象加鎖,生命週期結束調用析構函數時解鎖。

for (int i = 0; i < 100000; i++)
  {
   m.lock();
   lock_guard<mutex>m_guard(m,adopt_lock);
   a++;
  }
}

也可以加adopt_lock參數,接管已被lock的mutex對象。注意,不管是哪種方式構造出的lock_guard,都不要再調用mutex對象的lock(),unlock()了(雖然只要保證lock和unlock的順序和次數就不會出問題,但是沒必要這樣用)。

2.std::unique_lock

unique_lock也是 C++ 11 提供的模板了,完全代替lock_guard 的功能,並且更加靈活,功能更加豐富,但是相應的消耗比lock_guard大,效率低一些。定義如下:
template<class Mutex>class unique_lock;
unique_lock 有下面幾種構造方式:
(一)unique_lock() noexcept;
這種默認的構造函數,構造出來的對象不管理任何 mutex 對象。
(二) explicit unique_lock (mutex_type& m);
這種構造函數構造出來的unique_lock 對象接管一個沒有lock的mutex對象,並且在構造函數中調用mutex對象的lock函數,失敗會阻塞,直到lock成功。由於加了explicit ,這種構造方式只能顯式的構造。

#include<stdio.h>
#include<thread>
#include<mutex>
using namespace std;
mutex m;
void fun(int &a)
{
for (int i = 0; i < 100000; i++)
 {
  unique_lock<mutex>unique_m(m);
  a++;
 }
 }
 int main()
 {
 int a = 0;
 thread t1(fun, ref(a));
 thread t2(fun, ref(a));
 t1.join();
 t2.join();
 printf("%d\n", a);
 }

(三)unique_lock (mutex_type& m, try_to_lock_t tag);
這樣的構造函數,會在構造函數中調用mutex對象的try_lock函數,鎖失敗會立刻返回。
(四)unique_lock (mutex_type& m, defer_lock_t tag) noexcept;
這樣構造出來的unique_lock 只是單純的接管mutex對象,不會上鎖。
(五)unique_lock (mutex_type& m, adopt_lock_t tag);
這種構造會接管一個已經lock的mutex對象,就是在構造函數中不再調用mutex的lock函數。
(六)
template <class Rep, class Period>
unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);

這種構造函數會調用try_lock_for,在rel_time這個時間段內嘗試lock接管的mutex對象,超時會立即返回。
(七)
template <class Clock, class Duration>
unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);

這種構造函數會調用try_lock_until,在abs_time這個時間點之前嘗試lock接管的mutex對象,超時立即返回。
(八)unique_lock (unique_lock&& x);
更換mutex所有權,新創建出來的unique_lock對象會接管參數中對象的mutex對象。

其中,(二)(五)兩種在創建出對象後,mutex對象是lock狀態的,(一)(四)是非lock狀態的,其他的不定,不管任何mutex對象 ,一旦被 unique_lock對象接管後,不允許其他unique_lock對象接管了。

unique_lock 的其他成員函數
lock()
unlock()
try_lock()
try_lock_for()
try_lcok_until()
相較於lock_guard,unique_lock 可以隨時的lock和unlock,更加靈活,try_lock_for()會在一段時間內嘗試lock對象,超時立刻返回。
try_lcok_until()會在時間點之前嘗試lcok對象,超時立刻返回。

unique_lock交換所有權
可以用std::move 或者臨時對象來轉換歸屬權

  unique_lock<mutex>unique_m(m);
  unique_lock<mutex>unique_n(n);
  unique_n = move(unique_m);
unique_lock<mutex>unique_m(m); 
unique_m = unique_lock<mutex>(n);

在交換所有權之前,左值會將自己所有的mutex對象unlock,然後接管右值的mutex對象,並且不會改變其狀態。

unique_lock釋放mutex對象
成員函數release可以釋放所管理的mutex對象,調用之後,mutex對象不再歸屬當前unique_lock對象。但是並不改變mutex對象的狀態,要區分release與unlock的區別。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章