C++ 2.0——多线程的使用之lock_guard 与 unique_lock

C++ 标准为我们提供了以下基本的锁类型

  • lock_guard(C++11)
  • unique_lock(C++11)
  • shared_lock(C++14)
  • scoped_lock(C++17)

以及还提供了几个与锁类型相关的 Tag 类:

 

  • defer_lock_t不获得互斥的所有权
  • try_to_lock_t尝试获得互斥的所有权而不阻塞
  • adopt_lock_t假设调用方线程已拥有互斥的所有权

 struct defer_lock_t { };

  /// Try to acquire ownership of the mutex without blocking.
  struct try_to_lock_t { };

  /// Assume the calling thread has already obtained mutex ownership
  /// and manage it.
  struct adopt_lock_t { };

  constexpr defer_lock_t    defer_lock { };
  constexpr try_to_lock_t    try_to_lock { };
  constexpr adopt_lock_t    adopt_lock { };

std::lock_guard

类 lock_guard 是互斥体包装器,为在作用域块期间占有互斥提供便利 RAII 风格机制;创建 lock_guard 对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥;lock_guard 类不可复制。

template<typename _Mutex>
    class lock_guard
    {
    public:
      typedef _Mutex mutex_type;

      explicit lock_guard(mutex_type& __m) : _M_device(__m)
      { _M_device.lock(); }

      lock_guard(mutex_type& __m, adopt_lock_t) : _M_device(__m)
      { } // calling thread owns mutex

      ~lock_guard()
      { _M_device.unlock(); }

      lock_guard(const lock_guard&) = delete;
      lock_guard& operator=(const lock_guard&) = delete;

    private:
      mutex_type&  _M_device;
    };

在 lock_guard 对象构造时,传入的 Mutex 对象(即它所管理的 Mutex 对象)会被当前线程锁住。在lock_guard 对象被析构时,它所管理的 Mutex 对象会自动解锁,由于不需要程序员手动调用 lock 和 unlock 对 Mutex 进行上锁和解锁操作,因此这也是最简单安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的 Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码。

lock_guard 对象并不负责管理 Mutex 对象的生命周期,lock_guard 对象只是简化了 Mutex 对象的上锁和解锁操作,方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁。

简单的测试:

#include <iostream>
#include <mutex>

using namespace std;

#define M 10000000

class A 
{
public:
	A() :_count(0) {}

	void print() 
	{
		for (size_t i = 0; i < M; i++)
		{
			_mutex.lock();//加锁
            //adopt_lock标记当前已获得锁,lock_guard构造函数中并不会再次执行lock
			lock_guard<mutex> lock(_mutex,adopt_lock);
			_count++;
		}
	}

	void print2()
	{
		for (size_t i = 0; i < M; i++)
		{
			lock_guard<mutex> lock(_mutex);
			_count++;
		}
	}

private:
	mutex _mutex;
public:
	int	  _count;
};


int main() 
{
	A a1;

	thread t1(&A::print,&a1);
	thread t2(&A::print2, &a1);

	t1.join();
	t2.join();

	cout << " print count: " << a1._count << endl;

	return system("pause");

}

std::unique_lock

类 unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。

类 unique_lock 可移动,但不可复制——它满足可移动构造 (MoveConstructible) 可移动赋值 (MoveAssignable) 但不满足可复制构造 (CopyConstructible) 可复制赋值 (CopyAssignable) 

类 unique_lock 满足基本可锁定 (BasicLockable) 要求。若 Mutex 满足可锁定 (Lockable) 要求,则 unique_lock 亦满足可锁定 (Lockable) 要求(例如:能用于 std::lock ) ;若 Mutex 满足可定时锁定 (TimedLockable) 要求,则 unique_lock 亦满足可定时锁定 (TimedLockable) 要求。

1. 构造函数与析构函数

以下是unique_lock的构造函数与析构函数:

     unique_lock() noexcept
      : _M_device(0), _M_owns(false)
      { }

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

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

      unique_lock(mutex_type& __m, try_to_lock_t)
      : _M_device(&__m), _M_owns(_M_device->try_lock())
      { }

      unique_lock(mutex_type& __m, adopt_lock_t)
      : _M_device(&__m), _M_owns(true)
      {
	// XXX calling thread owns mutex
      }

      template<typename _Clock, typename _Duration>
	unique_lock(mutex_type& __m,
		    const chrono::time_point<_Clock, _Duration>& __atime)
	: _M_device(&__m), _M_owns(_M_device->try_lock_until(__atime))
	{ }

      template<typename _Rep, typename _Period>
	unique_lock(mutex_type& __m,
		    const chrono::duration<_Rep, _Period>& __rtime)
	: _M_device(&__m), _M_owns(_M_device->try_lock_for(__rtime))
	{ }

      ~unique_lock()
      {
	if (_M_owns)
	  unlock();
      }
default (1)
unique_lock() noexcept;
 
locking (2)
explicit unique_lock(mutex_type& m);
新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞。
try-locking (3)
unique_lock(mutex_type& m, try_to_lock_t tag);
新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.try_lock() 对 Mutex 对象进行上锁,但如果上锁不成功,并不会阻塞当前线程。
deferred (4)
unique_lock(mutex_type& m, defer_lock_t tag) noexcept;
新创建的 unique_lock 对象管理 Mutex 对象 m,但是在初始化的时候并不锁住 Mutex 对象。
adopting (5)
unique_lock(mutex_type& m, adopt_lock_t tag);
新创建的 unique_lock 对象管理 Mutex 对象 m, m 应该是一个已经被当前线程锁住的 Mutex 对象。(并且当前新创建的 unique_lock 对象拥有对锁(Lock)的所有权)。
locking for (6)
template <class Rep, class Period>
unique_lock(mutex_type& m, const chrono::duration<Rep,Period>& rel_time);
新创建的 unique_lock 对象管理 Mutex 对象 m,并试图通过调用 m.try_lock_for(rel_time) 来锁住 Mutex 对象一段时间(rel_time)。
locking until (7)
template <class Clock, class Duration>
unique_lock(mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);
新创建的 unique_lock 对象管理 Mutex 对象m,并试图通过调用 m.try_lock_until(abs_time) 来在某个时间点(abs_time)之前锁住 Mutex 对象。

2. copy assignment 和 copy constructor

   unique_lock 对象不能被拷贝和赋值。

  unique_lock(const unique_lock&) = delete;
  unique_lock& operator=(const unique_lock&) = delete;

3. move assignment 和 move constructor

    unique_lock(unique_lock&& __u) noexcept
      : _M_device(__u._M_device), _M_owns(__u._M_owns)
      {
	__u._M_device = 0;
	__u._M_owns = false;
      }

      unique_lock& operator=(unique_lock&& __u) noexcept
      {
	if(_M_owns)
	  unlock();

	unique_lock(std::move(__u)).swap(*this);

	__u._M_device = 0;
	__u._M_owns = false;

	return *this;
      }

新创建的 unique_lock 对象获得了由 x 所管理的 Mutex 对象的所有权(包括当前 Mutex 的状态)。调用 move 构造之后, x 对象如同通过默认构造函数所创建的,就不再管理任何 Mutex 对象了。

4. 成员函数

lock

锁定关联互斥
(公开成员函数)

try_lock

尝试锁定关联互斥,若互斥不可用则返回
(公开成员函数)

try_lock_for

试图锁定关联的可定时锁定 (TimedLockable) 互斥,若互斥在给定时长中不可用则返回
(公开成员函数)

try_lock_until

尝试锁定关联可定时锁定 (TimedLockable) 互斥,若抵达指定时间点互斥仍不可用则返回
(公开成员函数)

unlock

解锁关联互斥
(公开成员函数)

修改器

swap

与另一 std::unique_lock 交换状态
(公开成员函数)

release

将关联互斥解关联而不解锁它
(公开成员函数)

观察器

mutex

返回指向关联互斥的指针
(公开成员函数)

owns_lock

测试锁是否占有其关联互斥
(公开成员函数)

operator bool

测试锁是否占有其关联互斥
(公开成员函数)

简单的测试

#include <iostream>
#include <mutex>
using namespace std;

#define M 1000000

class A 
{
public:
	A() :count(0) {}

	void myPrint() 
	{

		for (int i = 0; i < M; i++) 
		{
			unique_lock<mutex> lock(_m_Mutex,try_to_lock);
			if (lock.owns_lock()) {
				count++;
				cout << "     已获得锁" << endl;
			}
			else
				cout << "未获得锁" << endl;
		}
	}



	int getCount() 
	{
		return count;
	}

private:
	mutex _m_Mutex;
	int count;
};

int main() 
{
	A a1;
	thread t1(&A::myPrint,&a1);
	thread t2(&A::myPrint,&a1);

	t1.join();
	t2.join();

	cout << "A::myPrint count: "<< a1.getCount() << endl;

	std::mutex foo, bar;
	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);        // 锁两个 unique_lock 而不死锁


	return system("pause");

}

 

 

以上参考:

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值