c++11 多線程(2)mutex 總結(jié)


接著上上節(jié) thread ,本節(jié)主要介紹mutex的內(nèi)容,練習代碼地址。
<mutex>:該頭文件主要聲明了與互斥量(mutex)相關(guān)的類,包括 std::mutex 系列類,std::lock_guard, std::unique_lock, 以及其他的類型和函數(shù)。

2、 <mutex>

mutex 頭文件中主要包含 Mutexes、lock 和相關(guān)的類型(Other types)和公共函數(shù)。

| (一)、 Mutexes |

mutex mutex互斥鎖是一個可鎖的對象,它被設(shè)計成在關(guān)鍵的代碼段需要獨占訪問時發(fā)出信號,從而阻止具有相同保護的其他線程并發(fā)執(zhí)行并訪問相同的內(nèi)存位置。
recursive_mutex 遞歸互斥鎖是一個可鎖的對象,就像互斥一樣,但是允許同一個線程獲得對互斥對象的多個級別的所有權(quán)。
timed_mutex 定時的互斥信號是一個時間可鎖定的對象,旨在當關(guān)鍵部分的代碼需要獨占訪問時,就像一個普通互斥,但另外支持定時try-lock請求。
recursive_timed_mutex 一個遞歸的互斥對象結(jié)合的特點recursive_mutex timed_mutex到單個類的特性:它同時支持由一個線程獲取多個鎖級別也定時try-lock請求。

上面是 mutex頭文件中 mutexs中的相關(guān)類。上面類主要是幾種鎖對象。

  • std::mutex
    • (constructor) 不允許拷貝;互斥對象不能被復(fù)制/移動,初始狀態(tài)為未鎖定。
          default (1) constexpr mutex() noexcept;
          copy [deleted] (2) mutex (const mutex&) = delete;
  • lock 鎖住互斥對象

    1. 如果互斥對象當前沒有被任何線程鎖定,則調(diào)用線程鎖定它(從這一點開始,直到它的成員解鎖被調(diào)用,線程擁有互斥對象)。
    2. 如果互斥鎖當前被另一個線程鎖定,則調(diào)用線程的執(zhí)行將被阻塞,直到其他線程解鎖(其他非鎖定線程繼續(xù)執(zhí)行它們)。
    3. 如果互斥鎖當前被相同的線程所鎖定,調(diào)用此函數(shù),則會產(chǎn)生死鎖(未定義的行為)。

    示例1

    // mutex::lock/unlock
    #include <iostream>       // std::cout
    #include <thread>         // std::thread
    #include <mutex>          // std::mutex
    std::mutex mtx;           // mutex for critical section
    void print_thread_id (int id) {
      // critical section (exclusive access to std::cout signaled by locking mtx):
      mtx.lock();
      std::cout << "thread #" << id << '\n';
      mtx.unlock();
    }
    
    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;
    }
    
  • try_lock 試圖鎖定互斥鎖,不阻塞。
    若互斥對象當前沒有被任何線程鎖定,則調(diào)用線程鎖定它;
    若互斥鎖當前被另一個線程鎖定,則該函數(shù)失敗并返回false,沒有阻塞;若互斥鎖當前被相同的線程所鎖定,調(diào)用此函數(shù),則會產(chǎn)生死鎖。
    示例2

    // mutex::try_lock example
    #include <iostream>       // std::cout
    #include <thread>         // std::thread
    #include <mutex>          // std::mutex
    
    volatile int counter (0); // non-atomic counter
    std::mutex mtx;           // locks access to counter
    
    void attempt_10k_increases () {
        for (int i=0; i<10000; ++i) {
            if (mtx.try_lock()) {   // only increase if currently not locked:
                  ++counter;
                  mtx.unlock();
            }
       }
    }
    
    int main ()
    {
      std::thread threads[10];
      // spawn 10 threads:
      for (int i=0; i<10; ++i)
        threads[i] = std::thread(attempt_10k_increases);
    
      for (auto& th : threads) th.join();
      std::cout << counter << " successful increases of the counter.\n";
    
      return 0;
    }
    
  • unlock
    解鎖互斥鎖,釋放所有權(quán)。

  • native_handle
    獲取本地處理,如果庫實現(xiàn)支持它,這個成員函數(shù)只存在于類互斥中。如果存在,它返回一個值用于訪問特定于實現(xiàn)的信息相關(guān)聯(lián)的對象。

  • recursive_mutex
    遞歸鎖基本函數(shù)同上,只是std::recursive_mutex 允許同一個線程對互斥量多次上鎖(即遞歸上鎖),來獲得對互斥量對象的多層所有權(quán),std::recursive_mutex 釋放互斥量時需要調(diào)用與該鎖層次深度相同次數(shù)的 unlock()。

  • timed_mutex
    timed_mutex鎖比較mutex所多了兩個成員函數(shù)try_lock_for 和 try_lock_until。

    • try_lock_for
      傳入時間段,在時間范圍內(nèi)未獲得所就阻塞住線程,如果在此期間其他線程釋放了鎖,則該線程可以獲得對互斥量的鎖,如果超時,則返回 false。
    • try_lock_until
      同上面的解釋,只是傳入?yún)?shù)為一個未來的一個時間點。

    示例3

    // timed_mutex::try_lock_for example
    #include <iostream>       // std::cout
    #include <chrono>         // std::chrono::milliseconds
    #include <thread>         // std::thread
    #include <mutex>          // std::timed_mutex
    
    std::timed_mutex mtx;
    
    void fireworks () {
      // waiting to get a lock: each thread prints "-" every 200ms:
      while (!mtx.try_lock_for(std::chrono::milliseconds(2))) {
        std::cout << "-";
      }
      // got a lock! - wait for 1s, then this thread prints "*"
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
      std::cout << "*\n";
      mtx.unlock();
    }
    
    int main ()
    {
      std::thread threads[10];
      // spawn 10 threads:
      for (int i=0; i<10; ++i) {
        threads[i] = std::thread(fireworks);
      }
      for (auto& th : threads) th.join();
      return 0;
    }
    

    示例4

    // timed_mutex::try_lock_until example
    #include <iostream>       // std::cout
    #include <chrono>         // std::chrono::system_clock
    #include <thread>         // std::thread
    #include <mutex>          // std::timed_mutex
    #include <ctime>          // std::time_t, std::tm, std::localtime, std::mktime
    
    std::timed_mutex cinderella;
    
    // gets time_point for next midnight:
    std::chrono::time_point<std::chrono::system_clock> midnight() {
      using std::chrono::system_clock;
      std::time_t tt = system_clock::to_time_t (system_clock::now());
      struct std::tm * ptm = std::localtime(&tt);
      ++ptm->tm_mday; ptm->tm_hour=0; ptm->tm_min=0; ptm->tm_sec=0;
      return system_clock::from_time_t (mktime(ptm));
    }
    
    void carriage() {
      if (cinderella.try_lock_until(midnight())) {
        std::cout << "ride back home on carriage\n";
        cinderella.unlock();
      }
      else
        std::cout << "carriage reverts to pumpkin\n";
    }
    
    void ball() {
      cinderella.lock();
      std::cout << "at the ball...\n";
      cinderella.unlock();
    }
    
    int main ()
    {
      std::thread th1 (ball);
      std::thread th2 (carriage);
      th1.join();
      th2.join();
      return 0;
    }
    
  • recursive_timed_mutex
    同上面分析,遞歸的時間鎖,允許同一個線程對互斥量多次上鎖(即遞歸上鎖), 不再贅述。

(二)、Locks 模板類

lock_guard Lock guard (class template ) 模版類
unique_lock Unique lock (class template ) 模版類
  • lock_guard
    定義: template <class Mutex> class lock_guard;
    鎖保護是一個通過將互斥對象保持鎖定來管理互斥對象的對象。
    在構(gòu)造上,互斥對象被調(diào)用線程鎖定,并且在銷毀時,互斥鎖被解鎖。它是最簡單的鎖,作為具有自動持續(xù)時間的對象特別有用,直到它的上下文結(jié)束。這樣,它保證在拋出異常時,可以正確地鎖定互斥對象。
    注意,lock_guard對象不以任何方式管理互斥對象的生命周期:互斥對象的持續(xù)時間至少要延長到鎖定它的lock_guard的銷毀為止。

模板參數(shù) Mutex 代表互斥量類型,例如 std::mutex 類型,它應(yīng)該是一個基本的 BasicLockable 類型,標準庫中定義幾種基本的 BasicLockable 類型,分別 std::mutex, std::recursive_mutex, std::timed_mutexstd::recursive_timed_mutex 以及 std::unique_lock。 (注:BasicLockable 類型的對象只需滿足兩種操作,lock 和 unlock,另外還有 Lockable 類型,在 BasicLockable 類型的基礎(chǔ)上新增了 try_lock 操作,因此一個滿足 Lockable 的對象應(yīng)支持三種操作:lock,unlock 和 try_lock;最后還有一種 TimedLockable 對象,在 Lockable 類型的基礎(chǔ)上又新增了 try_lock_for 和 try_lock_until 兩種操作,因此一個滿足 TimedLockable 的對象應(yīng)支持五種操作:lock, unlock, try_lock, try_lock_for, try_lock_until)。

構(gòu)造函數(shù)(constructor)
locking (1) explicit lock_guard (mutex_type& m);
adopting (2) lock_guard (mutex_type& m, adopt_lock_t tag);
copy deleted lock_guard (const lock_guard&) = delete;
(1)鎖定初始化:對象管理m,并鎖定它(通過調(diào)用m.lock())。
(2)采用初始化:對象管理m,它是已經(jīng)當前被構(gòu)造線程鎖定的互斥對象。
(3)復(fù)制構(gòu)造: 刪除(lock_guard對象不能被復(fù)制/移動)。
注:lock_guard的對象保持鎖定并管理m(通過調(diào)用它的成員解鎖)來解鎖它,當lock_guard的對象析構(gòu)的時候,mtx將會被解鎖。
示例5

  // constructing lock_guard with adopt_lock
  #include <iostream>       // std::cout
  #include <thread>         // std::thread
  #include <mutex>          // std::mutex, std::lock_guard, std::adopt_lock

  std::mutex mtx;           // mutex for critical section

  void print_thread_id (int id) {
    mtx.lock();
    std::lock_guard<std::mutex> lck (mtx, std::adopt_lock);
    std::cout << "thread #" << id << '\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;
  }
  • unique_lock
    定義: template <class Mutex> class unique_lock;
    unique_lock與 lock_guard 類相似,但它提供了更好的上鎖和解鎖控制。unique_lock 對象以獨占所有權(quán)的方式(官方叫做unique ownership)管理 mutex 對象的上鎖和解鎖操作;獨占所有權(quán),就是沒有其他的 unique_lock 對象同時擁有某個 mutex 對象的所有權(quán)。
    unique_lock對象在析構(gòu)的時候一定保證互斥量為解鎖狀態(tài);因此它作為具有自動持續(xù)時間的對象特別有用,因為它保證在拋出異常時,互斥對象被正確地解鎖。不過,注意unique_lock對象并不以任何方式管理互斥對象的生命周期:互斥對象的持續(xù)時間將延長至少直到unique_lock管理它的毀滅。
  1. 構(gòu)造函數(shù)(constructor):
       default       (1)    unique_lock() noexcept;
       locking       (2)    explicit unique_lock (mutex_type& m);
       try-locking   (3)    unique_lock (mutex_type& m, try_to_lock_t tag);
       deferred      (4)    unique_lock (mutex_type& m, defer_lock_t tag) noexcept;
       adopting      (5)    unique_lock (mutex_type& m, adopt_lock_t tag);
       locking for   (6)    template <class Rep, class Period> 
                            unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);
       locking until (7)    template <class Clock, class Duration>
                            unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);
       copy [deleted] (8)  unique_lock (const unique_lock&) = delete;
       move           (9)    unique_lock (unique_lock&& x);

下面我們來分別介紹以上各個構(gòu)造函數(shù):
(1) 默認構(gòu)造函數(shù)
unique_lock 對象不管理任何 Mutex 對象m。

(2) locking 初始化
unique_lock 對象管理 Mutex 對象 m,并調(diào)用 m.lock() 對 Mutex 對象進行上鎖,如果其他 unique_lock 對象已經(jīng)管理了m,該線程將會被阻塞。

(3) try-locking 初始化
unique_lock 對象管理 Mutex 對象 m,并調(diào)用 m.try_lock() 對 Mutex 對象進行上鎖,但如果上鎖不成功,不會阻塞當前線程。

(4) deferred 初始化
unique_lock 對象管理 Mutex 對象 m并不鎖住m。 m 是一個沒有被當前線程鎖住的 Mutex 對象。

(5) adopting 初始化
unique_lock 對象管理 Mutex 對象 m, m 應(yīng)該是一個已經(jīng)被當前線程鎖住的 Mutex 對象。(當前unique_lock 對象擁有對鎖(lock)的所有權(quán))。

(6) locking 一段時間(duration)
新創(chuàng)建的 unique_lock 對象管理 Mutex 對象 m,通過調(diào)用 m.try_lock_for(rel_time) 來鎖住 Mutex 對象一段時間(rel_time)。

(7) locking 直到某個時間點(time point)
新創(chuàng)建的 unique_lock 對象管理 Mutex 對象m,通過調(diào)用 m.try_lock_until(abs_time) 來在某個時間點(abs_time)之前鎖住 Mutex 對象。

(8) 拷貝構(gòu)造 [被禁用]
unique_lock 對象不能被拷貝構(gòu)造。

(9) 移動(move)構(gòu)造
新創(chuàng)建的 unique_lock 對象獲得了由 x 所管理的 Mutex 對象的所有權(quán)(包括當前 Mutex 的狀態(tài))。調(diào)用 move 構(gòu)造之后, x 對象如同通過默認構(gòu)造函數(shù)所創(chuàng)建的,就不再管理任何 Mutex 對象了。

示例6

  // unique_lock constructor example
  #include <iostream>       // std::cout
  #include <thread>         // std::thread
  #include <mutex>          // std::mutex, std::lock, std::unique_lock
                                        // std::adopt_lock, std::defer_lock
  std::mutex foo,bar;

  void task_a () {
    std::lock (foo,bar);         // simultaneous lock (prevents deadlock)
    std::unique_lock<std::mutex> lck1 (foo,std::adopt_lock);
    std::unique_lock<std::mutex> lck2 (bar,std::adopt_lock);
    std::cout << "task a\n";
    // (unlocked automatically on destruction of lck1 and lck2)
  }

  void task_b () {
    // foo.lock(); bar.lock(); // replaced by:
    std::unique_lock<std::mutex> lck1, lck2;
    lck1 = std::unique_lock<std::mutex>(bar,std::defer_lock);
    lck2 = std::unique_lock<std::mutex>(foo,std::defer_lock);
    std::lock (lck1,lck2);       // simultaneous lock (prevents deadlock)
    std::cout << "task b\n";
    // (unlocked automatically on destruction of lck1 and lck2)
  }

  int main ()
  {
    std::thread th1 (task_a);
    std::thread th2 (task_b);

    th1.join();
    th2.join();
  
    return 0;
  }
  1. 其他成員函數(shù)
Lock/unlock 上鎖和解鎖過程
lock Lock mutex (public member function ) 調(diào)用被托管的互斥對象的成員鎖
try_lock Lock mutex if not locked (public member function ) 嘗試上鎖
try_lock_for Try to lock mutex during time span (public member function ) 嘗試在時間段上鎖
try_lock_until Try to lock mutex until time point (public member function ) 嘗試在時間點到之前上鎖
unlock Unlock mutex (public member function ) 解鎖
Modifiers 修改
operator= Move-assign unique_lock (public member function ) 同move操作
swap Swap unique locks (public member function ) 交換兩個互斥對象
release Release mutex (public member function ) 釋放鎖
Observers 獲取操作
owns_lock Owns lock (public member function )
lock Return whether it owns a lock (public member function )
mutex Get mutex (public member function )

(1)std::unique_lock::lock
鎖定互斥對象,調(diào)用被托管的互斥對象的成員鎖。
對已經(jīng)被其他線程鎖定的互斥對象調(diào)用鎖,阻塞當前線程(等待),直到鎖釋放。當函數(shù)返回時,對象在互斥鎖上擁有一個鎖,如果調(diào)用鎖定失敗,system_error異常。
示例7

// unique_lock::lock/unlock
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock

std::mutex mtx;           // mutex for critical section

void print_thread_id (int id) {
  std::unique_lock<std::mutex> lck (mtx,std::defer_lock);
  // critical section (exclusive access to std::cout signaled by locking lck):
  lck.lock();
  std::cout << "thread #" << id << '\n';
  lck.unlock();
}

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;
}

(2)std::unique_lock::try_lock
如果互斥量沒有鎖定就鎖定;調(diào)用try_lock 管理mutex對象,并使用返回值設(shè)置擁有狀態(tài);如果擁有的狀態(tài)在調(diào)用前為真或者如果對象目前管理沒有互斥對象,函數(shù)拋出一個system_error異常。
示例8

// unique_lock::try_lock example
#include <iostream>       // std::cout
#include <vector>         // std::vector
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock

std::mutex mtx;           // mutex for critical section

void print_star () {
  std::unique_lock<std::mutex> lck(mtx,std::defer_lock);
  // print '*' if successfully locked, 'x' otherwise: 
  if (lck.try_lock())
    std::cout << '*';
  else                    
    std::cout << 'x';
}

int main ()
{
  std::vector<std::thread> threads;
  for (int i=0; i<500; ++i)
    threads.emplace_back(print_star);

  for (auto& x: threads) x.join();
  std::cout << "\n";
  return 0;
}

(3) std::unique_lock::try_lock_for

template <class Rep, class Period>
bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);

在時間范圍span內(nèi)鎖定互斥鎖;調(diào)用try_lock_for管理時間的mutex對象,并使用返回值設(shè)置擁有狀態(tài);如果在調(diào)用之前擁有狀態(tài)已經(jīng)真或者如果對象目前管理沒有互斥對象,函數(shù)拋出一個system_error異常。
示例9

// unique_lock::try_lock_for example
#include <iostream>       // std::cout
#include <chrono>         // std::chrono::milliseconds
#include <thread>         // std::thread
#include <mutex>          // std::timed_mutex, std::unique_lock, std::defer_lock

std::timed_mutex mtx;

void fireworks () {
  std::unique_lock<std::timed_mutex> lck(mtx,std::defer_lock);
  // waiting to get a lock: each thread prints "-" every 200ms:
  while (!lck.try_lock_for(std::chrono::milliseconds(200))) {
    std::cout << "-";
  }
  // got a lock! - wait for 1s, then this thread prints "*"
  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  std::cout << "*\n";
}

int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(fireworks);

  for (auto& th : threads) th.join();

  return 0;
}

(4)std::unique_lock::try_lock_until
同上面描述,傳入一個時間點而不是時間段。示例:同上改動,不再述。

(5)std::unique_lock::unlock
調(diào)用unlock對托管的mutex對象進行解鎖,并將擁有的狀態(tài)設(shè)置為false;如果調(diào)用之前擁有狀態(tài)是錯誤,函數(shù)拋出一個system_error異常與operation_not_permitted錯誤條件。
示例10

// unique_lock::lock/unlock
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock

std::mutex mtx;           // mutex for critical section

void print_thread_id (int id) {
  std::unique_lock<std::mutex> lck (mtx,std::defer_lock);
  // critical section (exclusive access to std::cout signaled by locking lck):
  lck.lock();
  std::cout << "thread #" << id << '\n';
  lck.unlock();
}

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;
}

(6) std::unique_lock::operator=

move (1)    unique_lock& operator= (unique_lock&& x) noexcept;
copy [deleted] (2) unique_lock& operator= (const unique_lock&) = delete;

使用x的mutex對象替換掉當前對象的mutex,同時獲取x的狀態(tài)。替換掉的x將不再有mutex對象,如果對象在調(diào)用之前對其托管的mutex對象擁有一個鎖,那么它的解鎖成員在被替換之前就被調(diào)用了。;unique_lock對象不能被復(fù)制。
示例11

// unique_lock::operator= example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock

std::mutex mtx;           // mutex for critical section

void print_fifty (char c) {
  std::unique_lock<std::mutex> lck;         // default-constructed
  lck = std::unique_lock<std::mutex>(mtx);  // move-assigned
  for (int i=0; i<50; ++i) { std::cout << c; }
  std::cout << '\n';
}

int main ()
{
  std::thread th1 (print_fifty,'*');
  std::thread th2 (print_fifty,'$');

  th1.join();
  th2.join();

  return 0;
}

(7) std::unique_lock::swap
與x交換內(nèi)容,包括托管的互斥對象和它們當前擁有的狀態(tài)。

(8) std::unique_lock::release
返回一個指向托管的互斥對象的指針,并釋放對它的所有權(quán);調(diào)用后unique_lock不再管理mutex對象(像默認構(gòu)造一樣);注意,該函數(shù)不會鎖定或釋放返回的互斥對象。
示例12

// unique_lock::release example
#include <iostream>       // std::cout
#include <vector>         // std::vector
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock

std::mutex mtx;
int count = 0;

void print_count_and_unlock (std::mutex* p_mtx) {
  std::cout << "count: " << count << '\n';
  p_mtx->unlock();
}

void task() {
  std::unique_lock<std::mutex> lck(mtx);
  ++count;
  print_count_and_unlock(lck.release());
}

int main ()
{
  std::vector<std::thread> threads;
  for (int i=0; i<10; ++i)
    threads.emplace_back(task);

  for (auto& x: threads) x.join();

  return 0;
}

(9) std::unique_lock::owns_lock
返回unique_lock對象擁有的一個鎖。unique_lock鎖定一個mutex,在沒有解鎖或者釋放unique_lock之前返回為真,其他情況返回為假;是unique_lock::operator bool的別名。
示例13

// unique_lock::operator= example
#include <iostream>       // std::cout
#include <vector>         // std::vector
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::try_to_lock

std::mutex mtx;           // mutex for critical section

void print_star () {
  std::unique_lock<std::mutex> lck(mtx,std::try_to_lock);
  // print '*' if successfully locked, 'x' otherwise: 
  if (lck.owns_lock())
    std::cout << '*';
  else                    
    std::cout << 'x';
}

int main ()
{
  std::vector<std::thread> threads;
  for (int i=0; i<500; ++i)
    threads.emplace_back(print_star);

  for (auto& x: threads) x.join();
  std::cout << "\n";
  return 0;
}

(10) std::unique_lock::operator bool
同上面。

(11) std::unique_lock::mutex
返回一個指向托管的mutex對象的指針。注意unique_lock不釋放互斥對象管理的所有權(quán)。如果它擁有一個互斥鎖,它仍然是負責釋放它在某一時刻(像當unique_lock被銷毀的時候)。
示例14

// unique_lock::mutex example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock

class MyMutex : public std::mutex {
  int _id;
public:
  MyMutex (int id) : _id(id) {}
  int id() {return _id;}
};

MyMutex mtx (101);

void print_ids (int id) {
  std::unique_lock<MyMutex> lck (mtx);
  std::cout << "thread #" << id << " locked mutex " << lck.mutex()->id() << '\n';
}

int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(print_ids,i+1);

  for (auto& th : threads) th.join();

  return 0;
}

| (三)、Othertypes其他類型 |

once_flag Flag argument type for call_once (class ) once_flag
adopt_lock_t Type of adopt_lock (class ) adopt_lock_t
defer_lock_t Type of defer_lock (class ) defer_lock_t
try_to_lock_t Type of try_to_lock (class ) try_to_lock_t
  • once_flag
    此類型的對象用作call_once的參數(shù)。
    在不同的線程中使用相同的對象在不同的調(diào)用上調(diào)用call_once,如果同時調(diào)用,則會單個執(zhí)行。
    它是一個不可復(fù)制的、不可移動的、可構(gòu)造的類。

    struct once_flag {
    constexpr once_flag() noexcept;
    once_flag (const once_flag&) = delete;
    once_flag& operator= (const once_flag&) = delete;
    };

  • adopt_lock_t
    這是一個空的類,用作adopt_lock的使用的類型。
    向unique_lock或lock_guard的構(gòu)造函數(shù)傳遞使用過的鎖,使該對象不鎖定互斥對象,并假設(shè)它已經(jīng)被當前線程鎖定。
    struct defer_lock_t {}; // 空類,只是作為adopt_lock類型

    constexpr adopt_lock_t adopt_lock {}; 
    

定義adopt_lock 值用作對unique_lock或lock_guard的構(gòu)造函數(shù)的可能參數(shù)。
使用adopt_lock構(gòu)造unique_lock對象不鎖定構(gòu)建中的互斥對象,只是假設(shè)它已經(jīng)被當前線程鎖定了;這個值是一個沒有狀態(tài)的編譯時常量,只是用來消除構(gòu)造函數(shù)簽名之間的歧義。

  • defer_lock_t
    這是一個空類,用作延遲鎖的類型。
    將延遲鎖傳遞給unique_lock的構(gòu)造函數(shù),使它不會在構(gòu)造上自動鎖定互斥對象,初始化對象為不擁有鎖。
    struct defer_lock_t {};
    constexpr defer_lock_t defer_lock {};
    定義defer_lock用于unique_lock的構(gòu)造函數(shù)的可能的參數(shù)。使用延遲鎖構(gòu)造的unique_lock對象不將互斥對象自動鎖定在構(gòu)造上,并初始化它們不擁有鎖。同上面的一樣,這個值是一個沒有狀態(tài)的編譯時常量,它只是用來消除構(gòu)造函數(shù)簽名之間的歧義。

  • try_to_lock_t
    try_to_lock_t是用于try_to_lock類型的空類。
    將try_to_lock傳遞給unique_lock的構(gòu)造函數(shù),使它通過調(diào)用它的try_lock成員來鎖定互斥對象,代替lock。
    struct try_to_lock_t {};
    constexpr try_to_lock_t try_to_lock {};
    try_to_lock 用于unique_lock的構(gòu)造函數(shù)的可能的參數(shù);使用try_to_lock構(gòu)造的unique_lock對象試圖通過調(diào)用其try_lock成員而不是鎖成員來鎖定互斥對象。

(四)、mutex中的函數(shù)

try_lock Try to lock multiple mutexes (function template ) 試鎖互斥量
lock Lock multiple mutexes (function template )
call_once Call function once (public member function ) call_once
  • try_lock
    std::try_lock是一個模版函數(shù):
      template <class Mutex1, class Mutex2, class... Mutexes>
      int try_lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

使用try_lock成員函數(shù)(非阻塞)式的鎖定對象a,b...等。函數(shù)為每個參數(shù)調(diào)用try_lock成員函數(shù)(首先是a,然后是b,最后是cde中的其他函數(shù)),直到所有調(diào)用都是成功的,或者只要調(diào)用失敗(返回false或拋出異常)。如果函數(shù)結(jié)束是因為調(diào)用失敗,則對所有調(diào)用try_lock成功的對象調(diào)用解鎖,函數(shù)將返回鎖定失敗的對象的參數(shù)序號。沒有對參數(shù)列表中的其余對象執(zhí)行其他調(diào)用。
示例15

// std::lock example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::try_lock

std::mutex foo,bar;

void task_a () {
  foo.lock();
  std::cout << "task a\n";
  bar.lock();
  // ...
  foo.unlock();
  bar.unlock();
}

void task_b () {
  int x = try_lock(bar,foo);
  if (x==-1) {
    std::cout << "task b\n";
    // ...
    bar.unlock();
    foo.unlock();
  }
  else {
    std::cout << "[task b failed: mutex " << (x?"foo":"bar") << " locked]\n";
  }
}

int main ()
{
  std::thread th1 (task_a);
  std::thread th2 (task_b);

  th1.join();
  th2.join();

  return 0;
}
  • lock
    std::lock同樣為一個模版函數(shù)
      template <class Mutex1, class Mutex2, class... Mutexes>
      void lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

鎖定所有的參數(shù)互斥對象,阻塞當前調(diào)用線程;該函數(shù)使用一個未指定的調(diào)用序列來鎖定對象,該序列調(diào)用其成員鎖、try_lock和解鎖,以確保所有參數(shù)都被鎖定在返回(不產(chǎn)生任何死鎖)。
如果函數(shù)不能鎖定所有對象(例如,因為其中一個內(nèi)部調(diào)用拋出異常),則函數(shù)首先解鎖所有成功鎖定的對象(如果有的話)。
示例16:

  // std::lock example
  #include <iostream>       // std::cout
  #include <thread>         // std::thread
  #include <mutex>          // std::mutex, std::lock

  std::mutex foo,bar;

  void task_a () {
    // foo.lock(); bar.lock(); // replaced by:
    std::lock (foo,bar);
    std::cout << "task a\n";
    foo.unlock();
    bar.unlock();
  }
  
  void task_b () {
    // bar.lock(); foo.lock(); // replaced by:
    std::lock (bar,foo);
    std::cout << "task b\n";
    bar.unlock();
    foo.unlock();
  }

  int main ()
  {
    std::thread th1 (task_a);
    std::thread th2 (task_b);

    th1.join();
    th2.join();

    return 0;
  }
  • call_once
    std::call_once 公有模版函數(shù)
       template <class Fn, class... Args>
          void call_once (once_flag& flag, Fn&& fn, Args&&... args);

call_once調(diào)用將args 作為fn的參數(shù)調(diào)用fn,除非另一個線程已經(jīng)(或正在執(zhí)行)使用相同的flag調(diào)用執(zhí)行call_once。如果已經(jīng)有一個線程使用相同flag調(diào)用call_once,會使得當前變?yōu)楸粍訄?zhí)行,所謂被動執(zhí)行不執(zhí)行fn也不返回直到恢復(fù)執(zhí)行后返回。這這個時間點上所有的并發(fā)調(diào)用這個函數(shù)相同的flag都是同步的。
注意,一旦一個活躍調(diào)用返回了,所有當前被動執(zhí)行和未來可能的調(diào)用call_once相同相同的flag也還不會成為積極執(zhí)行。
示例17

  // call_once example
  #include <iostream>       // std::cout
  #include <thread>         // std::thread, std::this_thread::sleep_for
  #include <chrono>         // std::chrono::milliseconds
  #include <mutex>          // std::call_once, std::once_flag

  int winner;
  void set_winner (int x) { winner = x; }
  std::once_flag winner_flag;

  void wait_1000ms (int id) {
    // count to 1000, waiting 1ms between increments:
    for (int i=0; i<1000; ++i)
      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    // claim to be the winner (only the first such call is executed):
    std::call_once (winner_flag,set_winner,id);
  }

  int main ()
  {
    std::thread threads[10];
    // spawn 10 threads:
    for (int i=0; i<10; ++i)
      threads[i] = std::thread(wait_1000ms,i+1);

    std::cout << "waiting for the first among 10 threads to count 1000   ms...\n";

    for (auto& th : threads) th.join();
    std::cout << "winner thread: " << winner << '\n';
  
    return 0;
  }

本文主要將mutex頭文件相關(guān),下篇c++11 多線程(3)atomic總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容