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_lock

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

void 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_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';

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

输出:

thread #1
thread #2
thread #4
thread #3
thread #7
thread #5
thread #6
thread #10
thread #8
thread #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_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';

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

输出:

count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
count: 7
count: 8
count: 9
count: 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_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;
}

输出:

thread #1 locked mutex 101
thread #2 locked mutex 101
thread #3 locked mutex 101
thread #4 locked mutex 101
thread #7 locked mutex 101
thread #5 locked mutex 101
thread #10 locked mutex 101
thread #8 locked mutex 101
thread #9 locked mutex 101
thread #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_lock

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

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

输出:

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

TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧