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


接著上上節(jié) thread 穗熬,本節(jié)主要介紹mutex的內(nèi)容雷滋,練習(xí)代碼地址
<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)鍵的代碼段需要獨(dú)占訪問時發(fā)出信號肤频,從而阻止具有相同保護(hù)的其他線程并發(fā)執(zhí)行并訪問相同的內(nèi)存位置叹括。
recursive_mutex 遞歸互斥鎖是一個可鎖的對象,就像互斥一樣宵荒,但是允許同一個線程獲得對互斥對象的多個級別的所有權(quán)汁雷。
timed_mutex 定時的互斥信號是一個時間可鎖定的對象,旨在當(dāng)關(guān)鍵部分的代碼需要獨(dú)占訪問時,就像一個普通互斥,但另外支持定時try-lock請求。
recursive_timed_mutex 一個遞歸的互斥對象結(jié)合的特點(diǎn)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. 如果互斥對象當(dāng)前沒有被任何線程鎖定岩臣,則調(diào)用線程鎖定它(從這一點(diǎn)開始溜嗜,直到它的成員解鎖被調(diào)用,線程擁有互斥對象)架谎。
    2. 如果互斥鎖當(dāng)前被另一個線程鎖定炸宵,則調(diào)用線程的執(zhí)行將被阻塞,直到其他線程解鎖(其他非鎖定線程繼續(xù)執(zhí)行它們)狐树。
    3. 如果互斥鎖當(dāng)前被相同的線程所鎖定,調(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 試圖鎖定互斥鎖,不阻塞野哭。
    若互斥對象當(dāng)前沒有被任何線程鎖定在塔,則調(diào)用線程鎖定它;
    若互斥鎖當(dāng)前被另一個線程鎖定拨黔,則該函數(shù)失敗并返回false蛔溃,沒有阻塞;若互斥鎖當(dāng)前被相同的線程所鎖定,調(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
    獲取本地處理秃臣,如果庫實(shí)現(xiàn)支持它,這個成員函數(shù)只存在于類互斥中哪工。如果存在,它返回一個值用于訪問特定于實(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ù)為一個未來的一個時間點(diǎn)。

    示例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;
    鎖保護(hù)是一個通過將互斥對象保持鎖定來管理互斥對象的對象。
    在構(gòu)造上晶姊,互斥對象被調(diào)用線程鎖定扒接,并且在銷毀時,互斥鎖被解鎖们衙。它是最簡單的鎖钾怔,作為具有自動持續(xù)時間的對象特別有用,直到它的上下文結(jié)束蒙挑。這樣宗侦,它保證在拋出異常時,可以正確地鎖定互斥對象忆蚀。
    注意矾利,lock_guard對象不以任何方式管理互斥對象的生命周期:互斥對象的持續(xù)時間至少要延長到鎖定它的lock_guard的銷毀為止姑裂。

模板參數(shù) Mutex 代表互斥量類型,例如 std::mutex 類型男旗,它應(yīng)該是一個基本的 BasicLockable 類型舶斧,標(biāo)準(zhǔn)庫中定義幾種基本的 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)當(dāng)前被構(gòu)造線程鎖定的互斥對象咐吼。
(3)復(fù)制構(gòu)造: 刪除(lock_guard對象不能被復(fù)制/移動)。
注:lock_guard的對象保持鎖定并管理m(通過調(diào)用它的成員解鎖)來解鎖它商佑,當(dāng)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 對象以獨(dú)占所有權(quán)的方式(官方叫做unique ownership)管理 mutex 對象的上鎖和解鎖操作;獨(dú)占所有權(quán)抓半,就是沒有其他的 unique_lock 對象同時擁有某個 mutex 對象的所有權(quán)喂急。
    unique_lock對象在析構(gòu)的時候一定保證互斥量為解鎖狀態(tài);因此它作為具有自動持續(xù)時間的對象特別有用笛求,因?yàn)樗WC在拋出異常時廊移,互斥對象被正確地解鎖。不過,注意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) 默認(rèn)構(gòu)造函數(shù)
unique_lock 對象不管理任何 Mutex 對象m狡孔。

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

(3) try-locking 初始化
unique_lock 對象管理 Mutex 對象 m徒爹,并調(diào)用 m.try_lock() 對 Mutex 對象進(jìn)行上鎖荚醒,但如果上鎖不成功,不會阻塞當(dāng)前線程隆嗅。

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

(5) adopting 初始化
unique_lock 對象管理 Mutex 對象 m踊沸, m 應(yīng)該是一個已經(jīng)被當(dāng)前線程鎖住的 Mutex 對象辛掠。(當(dāng)前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 直到某個時間點(diǎn)(time point)
新創(chuàng)建的 unique_lock 對象管理 Mutex 對象m纬朝,通過調(diào)用 m.try_lock_until(abs_time) 來在某個時間點(diǎn)(abs_time)之前鎖住 Mutex 對象个绍。

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

(9) 移動(move)構(gòu)造
新創(chuàng)建的 unique_lock 對象獲得了由 x 所管理的 Mutex 對象的所有權(quán)(包括當(dāng)前 Mutex 的狀態(tài))写穴。調(diào)用 move 構(gòu)造之后, x 對象如同通過默認(rèn)構(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 ) 嘗試在時間點(diǎn)到之前上鎖
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)用鎖欣孤,阻塞當(dāng)前線程(等待)馋没,直到鎖釋放。當(dāng)函數(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
同上面描述,傳入一個時間點(diǎn)而不是時間段翼悴。示例:同上改動缚够,不再述。

(5)std::unique_lock::unlock
調(diào)用unlock對托管的mutex對象進(jìn)行解鎖鹦赎,并將擁有的狀態(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對象替換掉當(dāng)前對象的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)容摘完,包括托管的互斥對象和它們當(dāng)前擁有的狀態(tài)姥饰。

(8) std::unique_lock::release
返回一個指向托管的互斥對象的指針,并釋放對它的所有權(quán);調(diào)用后unique_lock不再管理mutex對象(像默認(rèn)構(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)吕喘。如果它擁有一個互斥鎖,它仍然是負(fù)責(zé)釋放它在某一時刻(像當(dāng)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)被當(dā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)被當(dā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é)束是因?yàn)檎{(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ù)互斥對象,阻塞當(dāng)前調(diào)用線程卵渴;該函數(shù)使用一個未指定的調(diào)用序列來鎖定對象慧域,該序列調(diào)用其成員鎖、try_lock和解鎖浪读,以確保所有參數(shù)都被鎖定在返回(不產(chǎn)生任何死鎖)吊趾。
如果函數(shù)不能鎖定所有對象(例如,因?yàn)槠渲幸粋€內(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,會使得當(dāng)前變?yōu)楸粍訄?zhí)行屁奏,所謂被動執(zhí)行不執(zhí)行fn也不返回直到恢復(fù)執(zhí)行后返回。這這個時間點(diǎn)上所有的并發(fā)調(diào)用這個函數(shù)相同的flag都是同步的错负。
注意,一旦一個活躍調(diào)用返回了,所有當(dāng)前被動執(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市折联,隨后出現(xiàn)的幾起案子粒褒,更是在濱河造成了極大的恐慌,老刑警劉巖诚镰,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奕坟,死亡現(xiàn)場離奇詭異,居然都是意外死亡清笨,警方通過查閱死者的電腦和手機(jī)月杉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抠艾,“玉大人苛萎,你說我怎么就攤上這事〖旌牛” “怎么了腌歉?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長齐苛。 經(jīng)常有香客問我究履,道長,這世上最難降的妖魔是什么脸狸? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任最仑,我火速辦了婚禮,結(jié)果婚禮上炊甲,老公的妹妹穿的比我還像新娘泥彤。我一直安慰自己,他們只是感情好卿啡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布吟吝。 她就那樣靜靜地躺著,像睡著了一般颈娜。 火紅的嫁衣襯著肌膚如雪剑逃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天官辽,我揣著相機(jī)與錄音蛹磺,去河邊找鬼。 笑死同仆,一個胖子當(dāng)著我的面吹牛萤捆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼俗或,長吁一口氣:“原來是場噩夢啊……” “哼市怎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辛慰,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤区匠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帅腌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驰弄,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年狞膘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片什乙。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡挽封,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出臣镣,到底是詐尸還是另有隱情辅愿,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布忆某,位于F島的核電站点待,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏弃舒。R本人自食惡果不足惜癞埠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望聋呢。 院中可真熱鬧苗踪,春花似錦、人聲如沸削锰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽器贩。三九已至颅夺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛹稍,已是汗流浹背吧黄。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唆姐,地道東北人稚字。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胆描。 傳聞我的和親對象是個殘疾皇子瘫想,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容