Bootstrap

std::unique_lock的两个使用小技巧

unique_lock的简单介绍

unique_lock类里维护了一个mutex对象。在unique_lock类拥有多个构造函数,这里只放两个本文涉及的构造函数。

explicit unique_lock(mutex_type& __m)
: _M_device(std::__addressof(__m)), _M_owns(false)
{
	lock();
	_M_owns = true;
}

该构造函数会直接对mutex对象加锁。

      unique_lock(mutex_type& __m, defer_lock_t) noexcept
      : _M_device(std::__addressof(__m)), _M_owns(false)
      { }

这个构造函数不进行加锁操作。其中第二个参数defer_lock_t是一个空类。

在unique_lock类的析构函数里,对mutex对象进行了解锁操作。

~unique_lock()
{
	if (_M_owns)
	unlock();
}

看完了unique_lock类的构造函数和析构函数就可以引出我想说明的两个小技巧了。

技巧一:

直接手动加解锁。直接看代码。

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

需要注意的是,这里要加std::defer_lock参数。这样会调用第二个构造函数,不对mutex进行加锁操作。后面由自己手动操作。好处是可以自己选择需要加解锁的代码段。

#include <iostream>
#include <thread>

int v = 1;

void critical_section(int change_v) {
    static std::mutex mtx;
    std::unique_lock<std::mutex> lock(mtx);
    // do contention operations
    v = change_v;
    std::cout << v << std::endl;
    // release the lock
    lock.unlock();

    // during this period,
    // others are allowed to acquire v

    // start another group of contention operations
    // lock again
    lock.lock();
    v += 1;
    std::cout << v << std::endl;
}

int main() {
    std::thread t1(critical_section, 2), t2(critical_section, 3);
    t1.join();
    t2.join();
    return 0;
}

也可以使用上面代码的方式确定加锁的位置。注意此时unique_lock构造时是加锁的。

技巧二:

这里做了一些改动,让unique_lock在构造时加锁,析构时解锁。为了确定需要加解锁的代码段,我们用{}花括号把代码段括起来。

// 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) {
  
  // critical section (exclusive access to std::cout signaled by locking lck):
  
  {
  	std::unique_lock<std::mutex> lck (mtx);
  	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;
}

关注公众号《首飞》回复“机器人”获取精心推荐的C/C++,Python,Docker,Qt,ROS1/2等机器人行业常用技术资料。

;