unique_lock 取代 lock_guard

  • std::lock_guard 和 std::unique_lock 都能实现主动加锁与解锁性能。
  • std::unique_lock 外部持有 mutex 的状态(locked,unlocked),因而比 lock_guard 应用更加灵便但同时更占用空间、速度更慢。
// unique_lock example#include <iostream>       // std::cout#include <thread>         // std::thread#include <mutex>          // std::mutex, std::unique_lockstd::mutex mtx;           // mutex for critical sectionvoid print_block (int n, char c) {  // critical section (exclusive access to std::cout signaled by lifetime of lck):  std::unique_lock<std::mutex> lck (mtx);  for (int i=0; i<n; ++i) {       std::cout << c;   }  std::cout << '\n';}int main (){  std::thread th1 (print_block,50,'*');  std::thread th2 (print_block,50,'$');  th1.join();  th2.join();  return 0;}

输入:

**************************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

unique_lock 的属性参数

属性形容
通过调用成员lock来锁定互斥对象
try_lock通过调用成员try_lock尝试锁定互斥对象
defer_lockunique_lock 对象不会在结构时主动锁定互斥对象,将其初始化未不领有锁
adopt_lockunique_lock对象不会在结构时锁定互斥对象,而是假设调用线程曾经领有互斥锁的所有权。
try_to_lock
std::unique_lock<std::mutex>lck (mutex,std::try_to_lock);
defer_lock
std::unique_lock<std::mutex>lck (mutex,std::defer_lock);// ...lck.lock();// ...lck.unlock();// ...lck.lock();
adopt_lock
mutex.lock();// ......std::unique_lock<std::mutex>lck (mutex,std::adopt_lock);

std::unique_lock 的成员函数

std::unique_lock::owns_lock()

bool owns_lock() const noexcept;

性能:领有锁

  • 返回对象是否领有锁。
  • 如果托管互斥对象以由 unique_lock 对象锁定,并且尔后未解锁或开释,则返回 true。
  • 在其它状况下,都返回 false。
  • 这是 unique_lock :: operator bool的别名。

参数

返回值

  • 如果对象在托管互斥对象上领有锁,则返回 true, 否则返回 false。
// 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_lockstd::mutex mtx;           // mutex for critical sectionvoid 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';    std::cout << '\n';}int main (){  std::vector<std::thread> threads;  for (int i=0; i<10; ++i)    threads.emplace_back(print_star);  for (auto& x: threads) x.join();  return 0;}

输入:

**********

std::unique_lock::lock()

template <class Mutex1, class Mutex2, class... Mutexes>  void lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

性能:锁定多个互斥锁

  • 锁定所有作为参数的对象,并在必要时阻塞调用线程。
  • 该函数用一个未指定序列去调用他们的 lock, try_lock 和 unlock 成员锁定对象,以确保所有参数在返回时都被锁定(不产生任何死锁)。
  • 如果该函数无奈锁定所有对象(例如,因为外部调用之一引发了异样),则该函数会在失败之前首先解锁胜利锁定的所有对象(如果有)。

参数

a, b, cde

  • 尝试锁定的对象,Mutex1,Mutex2和Mutexes 应该是能够锁定的类型。

返回值

std::unique_lock::unlock()

void unlock();

性能:解锁互斥锁

  • 调用托管互斥对象的成员解锁,并将领有状态设置为 false。
  • 如果在调用之前领有状态为 false,则该函数将引发 system_error 异样。

返回值

// unique_lock::lock/unlock#include <iostream>       // std::cout#include <thread>         // std::thread#include <mutex>          // std::mutex, std::unique_lock, std::defer_lockstd::mutex mtx;           // mutex for critical sectionvoid 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;}

输入:

thread #1thread #2thread #4thread #3thread #7thread #5thread #6thread #10thread #8thread #9

std::unique_lock::try_lock()

template <class Mutex1, class Mutex2, class... Mutexes>  int try_lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

性能:尝试去锁定多个对象

  • 尝试应用其 try_lock 成员函数锁定所有作为参数传递的对象(非阻塞)。
  • 该函数为每个参数(首先是a,而后是b,最初是cde中的其它参数,以雷同的程序)调用 try_lock 成员函数,直到所有调用都胜利或者其中一个调用失败(通过返回谬误或引发异样)。
  • 如果函数因为调用失败而完结,则对 try_lock 调用胜利的所有对象都将解锁,并且该函数返回锁定失败的对象的参数顺序号。不会对参数列表中的其余对象执行进一步的调用。

参数

a, b, cde

  • 尝试锁定的对象,Mutex1,Mutex2和Mutexes 应该是能够锁定的类型。

返回值

  • 如果函数胜利锁定了所有对象,则返回 -1。
  • 否则,该函数将返回未定锁定对象的索引(0示意a,1示意b)
// 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_lockstd::mutex mtx;           // mutex for critical sectionvoid 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';    std::cout << '\n';}int main (){  std::vector<std::thread> threads;  for (int i=0; i<10; ++i)    threads.emplace_back(print_star);  for (auto& x: threads) x.join();  return 0;}

输入:

**********

std::unique_lock::release()

mutex_type* release() noexcept;

性能:开释互斥锁

  • 返回指向退管互斥对象的指针,从而开释对其的所有权。
  • 调用之后, unique_lock 对象不再治理任何互斥对象(即,其状态与默认结构的状态雷同)。
  • 请留神,此函数不会锁定或解锁返回的互斥量。

参数

返回值

  • 指向调用之前由 unique_lock 治理的互斥对象的指针。
// unique_lock::release example#include <iostream>       // std::cout#include <vector>         // std::vector#include <thread>         // std::thread#include <mutex>          // std::mutex, std::unique_lockstd::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;}

输入:

count: 1count: 2count: 3count: 4count: 5count: 6count: 7count: 8count: 9count: 10

std::unique_lock::mutex

mutex_type* mutex() const noexcept;

性能:获取互斥量

  • 返回指向托管互斥对象的指针。
  • 请留神,unique_lock 不会开释对托管互斥量的所有权。即,如果它领有互斥锁,则依然负责在某个时刻开释锁(例如销毁它时)

参数

返回值

指向由 unique_lock 治理的互斥对象的指针。

// unique_lock::mutex example#include <iostream>       // std::cout#include <thread>         // std::thread#include <mutex>          // std::mutex, std::unique_lock, std::defer_lockclass 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;}

输入:

thread #1 locked mutex 101thread #2 locked mutex 101thread #3 locked mutex 101thread #4 locked mutex 101thread #7 locked mutex 101thread #5 locked mutex 101thread #10 locked mutex 101thread #8 locked mutex 101thread #9 locked mutex 101thread #6 locked mutex 101

std::unique_lock::operator =

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

性能:挪动赋值

  • 用 x 中的一个替换托管的互斥对象,包含其领有的状态。
  • 如果对象在调用之前在其托管互斥对象上领有锁,则在替换之前将调用其unlock成员。
  • x 放弃与默认结构雷同的状态(指没有托管对象)。
  • unique_lock 对象无奈赋值。

参数

x

  • 另一个 unique_lock 对象。

返回值

  • *this。
// unique_lock::operator= example#include <iostream>       // std::cout#include <thread>         // std::thread#include <mutex>          // std::mutex, std::unique_lockstd::mutex mtx;           // mutex for critical sectionvoid print_fifty () {  std::unique_lock<std::mutex> lck;         // default-constructed  lck = std::unique_lock<std::mutex>(mtx);  // move-assigned  for (int i=0; i<50; ++i)  {      if (lck.owns_lock())        std::cout << '*';      else         std::cout << '#';  }  std::cout << '\n';}int main (){  std::thread th1 (print_fifty);  th1.join();  return 0;}

输入:

**************************************************