互斥量死锁出现和lock_guard类,unique_lock类的使用解析

1.互斥量死锁

下面的程序会出现一个问题,死锁。t1得到m1所有权的同时,t2得到了m2所有权,t1需要等待t2释放m2的所有权,t2需要等待t1释放m1的所有权,会出现互相等待的一个过程,称之为死锁。

===================================

死锁代码示例:

#include<i0stream>
#include<thread>
#include<mutex>
 
std::mutex m1,m2;
int a = 0;
 
void add_1(){
   for(i=0;i<50;i++)
   {
      m1.lock(); 
      m2.lock(); 
      m1.unlock();
      m2.unlock();
   }  
}

void add_2(){

   m2.lock(); 
   m1.lock(); 
   m1.unlock();
   m2.unlock();  
}
 
int main()
{
    std thread t1 (add_1);
    std thread t1 (add_2);
   
    t1.join();
    t2.join();
 
   std::cout<< "helloworld" <<std::endl;
   return 0;
}

=======================================

2.lock_guard函数

lock_guard是一种在作用域内控制可锁对象所有权的类型。lock_guard 是不可移动的,即不能拷贝、赋值、移动,只能通过构造函数初始化和析构函数销毁。

lock_guard具有两种构造方法:

lock_guard(mutex& m)
lock_guard(mutex& m, adopt_lock)

========================================

lock_guard源码如下:

template <class _Mutex>
class lock_guard { // class with destructor that unlocks a mutex
public:
    using mutex_type = _Mutex;
	//无adopt_lock参数,构造时加锁
    explicit lock_guard(_Mutex& _Mtx) : _MyMutex(_Mtx) { // construct and lock
        _MyMutex.lock();
    }
	//有adopt_lock参数,构造时不加锁
    lock_guard(_Mutex& _Mtx, adopt_lock_t) : _MyMutex(_Mtx) {} // construct but don't lock
	//析构解锁
    ~lock_guard() noexcept {
        _MyMutex.unlock();
    }
	//屏蔽拷贝构造
    lock_guard(const lock_guard&) = delete; 
    lock_guard& operator=(const lock_guard&) = delete; 

private:
    _Mutex& _MyMutex;
};

===============================================

lock_guard类有以下几个重要成员函数:

explicit lock_guard(_Mutex& _Mtx):
该构造函数会在创建lock_guard对象时自动上锁互斥锁_Mtx。
它使用互斥锁的lock()成员函数进行上锁操作。

lock_guard(_Mutex& _Mtx, adopt_lock_t):
该构造函数用于在已经上锁的情况下创建lock_guard对象。
在这种情况下,不需要再次上锁互斥锁,因此该构造函数空实现。

~lock_guard() _NOEXCEPT:
析构函数会在lock_guard对象销毁时自动解锁互斥锁_Mtx。
它使用互斥锁的unlock()成员函数进行解锁操作。

lock_guard(const lock_guard&) = delete和lock_guard& operator=(const lock_guard&) = delete:禁用拷贝构造函数和拷贝赋值运算符,
确保lock_guard对象不可拷贝。

lock_guard禁用了拷贝构造函数和拷贝赋值运算符,意味着它不支持拷贝语义,只能通过直接创建对象来使用。这样可以避免多个lock_guard对象同时管理同一个互斥锁而导致的错误行为。

================================================

lock_guard实现代码示例:

#include<iostream>
#include<thread>
#include<mutex>
 
int shared_data = 0;
std::mutex mtx; 
void add_1(){
   for(i=0;i<5000;i++)
   {
     std::lock_guard<std::mutex>lg(mtx);
     shared_data++;
   }  
}

int main()
{
    std thread t1 (add_1);
    std thread t1 (add_1);
   
    t1.join();
    t2.join();
 
   std::cout<< "shared_data" <<std::endl;
   return 0;
}

======================================

3.unique_lock函数

unique_lock 和lock_guard不同,它是可移动的,可以拷贝、赋值、移动。unique_lock 提供了更多的控制锁的行为,比如锁超时、不锁定、条件变量等。

unique_lock 比 lock_guard 更重,因为它有更多的功能,更多的开销。如果只需要简单的互斥保护,使用 lock_guard 更好。unique_lock 支持手动解锁,而 lock_guard 不支持。

#include<iostream>
#include<thread>
#include<mutex>
 
int shared_data = 0;
std::mutex mtx; 
void add_1(){
   for(i=0;i<5000;i++)
   {
     std::lock_guard<std::mutex>lg(mtx);
     shared_data++;
   }  
}

int main()
{
    std thread t1 (add_1);
    std thread t1 (add_1);
   
    t1.join();
    t2.join();
 
   std::cout<< "shared_data" <<std::endl;
   return 0;
}

=================================

unique_lock部分源码如下:

template<class _Mutex>
	class unique_lock
	{	// whizzy class with destructor that unlocks mutex
public:
	typedef unique_lock<_Mutex> _Myt;
	typedef _Mutex mutex_type;
 
	// CONSTRUCT, ASSIGN, AND DESTROY
	unique_lock() _NOEXCEPT
		: _Pmtx(0), _Owns(false)
		{	// default construct
		}
 
	explicit unique_lock(_Mutex& _Mtx)
		: _Pmtx(&_Mtx), _Owns(false)
		{	// construct and lock
		_Pmtx->lock();
		_Owns = true;
		}
 
	unique_lock(_Mutex& _Mtx, adopt_lock_t)
		: _Pmtx(&_Mtx), _Owns(true)
		{	// construct and assume already locked
		}
 
	unique_lock(_Mutex& _Mtx, defer_lock_t) _NOEXCEPT
		: _Pmtx(&_Mtx), _Owns(false)
		{	// construct but don't lock
		}
 
	unique_lock(_Mutex& _Mtx, try_to_lock_t)
		: _Pmtx(&_Mtx), _Owns(_Pmtx->try_lock())
		{	// construct and try to lock
		}
 
	template<class _Rep,
		class _Period>
		unique_lock(_Mutex& _Mtx,
			const chrono::duration<_Rep, _Period>& _Rel_time)
		: _Pmtx(&_Mtx), _Owns(_Pmtx->try_lock_for(_Rel_time))
		{	// construct and lock with timeout
		}
 
	template<class _Clock,
		class _Duration>
		unique_lock(_Mutex& _Mtx,
			const chrono::time_point<_Clock, _Duration>& _Abs_time)
		: _Pmtx(&_Mtx), _Owns(_Pmtx->try_lock_until(_Abs_time))
		{	// construct and lock with timeout
		}
 
	unique_lock(_Mutex& _Mtx, const xtime *_Abs_time)
		: _Pmtx(&_Mtx), _Owns(false)
		{	// try to lock until _Abs_time
		_Owns = _Pmtx->try_lock_until(_Abs_time);
		}
 
	unique_lock(unique_lock&& _Other) _NOEXCEPT
		: _Pmtx(_Other._Pmtx), _Owns(_Other._Owns)
		{	// destructive copy
		_Other._Pmtx = 0;
		_Other._Owns = false;
		}
 
	unique_lock& operator=(unique_lock&& _Other)
		{	// destructive copy
		if (this != &_Other)
			{	// different, move contents
			if (_Owns)
				_Pmtx->unlock();
			_Pmtx = _Other._Pmtx;
			_Owns = _Other._Owns;
			_Other._Pmtx = 0;
			_Other._Owns = false;
			}
		return (*this);
		}
 
	~unique_lock() _NOEXCEPT
		{	// clean up
		if (_Owns)
			_Pmtx->unlock();
		}
 
	unique_lock(const unique_lock&) = delete;
	unique_lock& operator=(const unique_lock&) = delete;
 
	// LOCK AND UNLOCK
	void lock()
		{	// lock the mutex
		_Validate();
		_Pmtx->lock();
		_Owns = true;
		}
 
	bool try_lock()
		{	// try to lock the mutex
		_Validate();
		_Owns = _Pmtx->try_lock();
		return (_Owns);
		}
 
	template<class _Rep,
		class _Period>
		bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time)
		{	// try to lock mutex for _Rel_time
		_Validate();
		_Owns = _Pmtx->try_lock_for(_Rel_time);
		return (_Owns);
		}
 
	template<class _Clock,
		class _Duration>
		bool try_lock_until(
			const chrono::time_point<_Clock, _Duration>& _Abs_time)
		{	// try to lock mutex until _Abs_time
		_Validate();
		_Owns = _Pmtx->try_lock_until(_Abs_time);
		return (_Owns);
		}
 
	bool try_lock_until(const xtime *_Abs_time)
		{	// try to lock the mutex until _Abs_time
		_Validate();
		_Owns = _Pmtx->try_lock_until(_Abs_time);
		return (_Owns);
		}
 
	void unlock()
		{	// try to unlock the mutex
		if (!_Pmtx || !_Owns)
			_THROW_NCEE(system_error,
				_STD make_error_code(errc::operation_not_permitted));
 
		_Pmtx->unlock();
		_Owns = false;
		}
 
	// MUTATE
	void swap(unique_lock& _Other) _NOEXCEPT
		{	// swap with _Other
		_STD swap(_Pmtx, _Other._Pmtx);
		_STD swap(_Owns, _Other._Owns);
		}
 
	_Mutex *release() _NOEXCEPT
		{	// disconnect
		_Mutex *_Res = _Pmtx;
		_Pmtx = 0;
		_Owns = false;
		return (_Res);
		}
 
	// OBSERVE
	bool owns_lock() const _NOEXCEPT
		{	// return true if this object owns the lock
		return (_Owns);
		}
 
	explicit operator bool() const _NOEXCEPT
		{	// return true if this object owns the lock
		return (_Owns);
		}
 
	_Mutex *mutex() const _NOEXCEPT
		{	// return pointer to managed mutex
		return (_Pmtx);
		}
 
private:
	_Mutex *_Pmtx;
	bool _Owns;
 
	void _Validate() const
		{	// check if the mutex can be locked
		if (!_Pmtx)
			_THROW_NCEE(system_error,
				_STD make_error_code(errc::operation_not_permitted));
 
		if (_Owns)
			_THROW_NCEE(system_error,
				_STD make_error_code(errc::resource_deadlock_would_occur));
		}
	};
 
	// SWAP
template<class _Mutex>
	void swap(unique_lock<_Mutex>& _Left,
		unique_lock<_Mutex>& _Right) _NOEXCEPT
	{	// swap _Left and _Right
	_Left.swap(_Right);
	}

======================================

unique_lock类有以下几个重要成员函数

lock():
尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,
则当前线程会被阻塞,直到互斥量被成功加锁。

try_lock():
尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,
则函数立即返回false,否则返回true。

try_lock_for(const std::chrono::duration<Rep, Period>& rel_time):
尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,
则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间。

try_lock_until(
const std::chrono::time_point<Clock, 
Duration>& abs_ti me):
尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,
则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间点。

==================================

unique_lock传入std::defer_lock,调用另一个不加锁的构造函数,上锁和解锁都需要自己动手实现,这样做的好处就是可以自己实现时间锁。

try_lock_for实现代码如下:

#include<iostream>
#include<thread>
#include<mutex>
 
int shared_data = 0;
std::time_mutex mtx; 
void add_1(){
   for(i=0;i<5000;i++)
   {
     std::unique_lock<std::time_mutex>lg(mtx,std::defer_lock);
    
      if( lg.try_lock_for(std::chrono::ceconds(2))){

        std::this_thread::sleep_for(std::chrono::seconds(1))
        //2s后还未获得锁的所有权,直接返回。
      
        shared_data++;
      }
   }  
}

int main()
{
    std thread t1 (add_1);
    std thread t1 (add_1);
   
    t1.join();
    t2.join();
 
   std::cout<< "shared_data" <<std::endl;      //打印出3
   return 0;
}

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肖爱Kun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值