基于多线程并发-STL之线程同步-互斥(mutex)

锁的理解:提供了以排他方式防止数据结构被并发修改的方法。

一、mutex类

mutex类源码

class _Mutex_base
	{	// base class for all mutex types
public:
	_Mutex_base(int _Flags = 0) noexcept;
	~_Mutex_base() noexcept;

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

	void lock();
	_NODISCARD bool try_lock();
	void unlock();

	typedef void *native_handle_type;
	_NODISCARD native_handle_type native_handle();

private:
	friend condition_variable;
	friend condition_variable_any;

	aligned_storage_t<_Mtx_internal_imp_size,
		_Mtx_internal_imp_alignment> _Mtx_storage;
		
	_Mtx_t _Mymtx() noexcept;
	};
class mutex
	: public _Mutex_base
	{	// class for mutual exclusion
public:
	/* constexpr */ mutex() noexcept	// TRANSITION
		: _Mutex_base()
		{	// default construct
		}

	mutex(const mutex&) = delete;
	mutex& operator=(const mutex&) = delete;
	};

类属性和接口说明
1、#include //头文件
2、std::mutex my_mutex; //创建对象,构造
3、my_mutex.lock(); //加锁,基类方法
4、my_mutex.unlock(); //解锁,基类方法
5、my_mutex.try_lock(); //尝试给互斥量加锁,返回值为是否拿到锁,基类方法
6、mutex类和其基类均无拷贝、拷贝赋值语义
7、nativehandle函数允许使用平台API直接操作低层实现,一般不使用。基类私有成员非外包接口,暂时不研究

二、lock_guard类模板

lock_guard类源码

template<class _Mutex>
	class lock_guard
	{	// class with destructor that unlocks a mutex
public:
	using mutex_type = _Mutex;

	explicit lock_guard(_Mutex& _Mtx)
		: _MyMutex(_Mtx)
		{	// construct and lock
		_MyMutex.lock();
		}

	lock_guard(_Mutex& _Mtx, adopt_lock_t)
		: _MyMutex(_Mtx)
		{	// construct but don't lock
		}

	~lock_guard() noexcept
		{	// unlock
		_MyMutex.unlock();
		}

	lock_guard(const lock_guard&) = delete;
	lock_guard& operator=(const lock_guard&) = delete;
private:
	_Mutex& _MyMutex;
	};

类属性和接口说明
1、可直接取代lock()和unlock(),不需要加锁和解锁组合使用

//创建方式
std::lock_guard<std::mutex> sbguard(my_mutex);

2、创建时调用lock(),出作用域析构调用unlock()
3、可选第二构造参数std::adopt_lock_t类型,不上锁构造函数(构造内不调用lock()),说明互斥量mutex已经lock
4、无拷贝、拷贝赋值语义

三、死锁

1、发生情况:
线程A被互斥锁1锁住,正要去lock锁2,线程B被互斥锁2锁住,正要去lock锁1;
线程A会等锁2unlock,线程B会等锁1unlock,此时则发生死锁。
2、处理方法:
保持锁的顺序一致,或者不在一块代码交织,单个锁则不会发生死锁
std::lock()函数模板锁定多个锁,但没有死锁。
std::lock()与mutex对象搭配还需手动unlock()。
std::lock()与lock_guard类模板搭配使用,需要调用传入std::adopt_lock_t 类型的第二参数的不上锁构造函数,因为std::lock()时已上锁。
3、避免死锁方法
1)避免嵌套锁
2)在持有锁时,避免调用用户提供代码
3)以固定的顺序获取锁
4)使用层次锁
4、锁属性标记
1)struct adopt_lock_t;//表明已采用锁
前提是已经lock。
2)struct defer_lock_t;//表明推迟锁
初始化了一个没有加锁的mutex。
前提是没先lock。
3)struct try_to_lock_t;//表示试图锁定
尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,并不阻塞。前提是没先lock。

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

五、unique_lock类模板

unique_lock类源码

template<class _Mutex>
	class unique_lock
	{	// whizzy class with destructor that unlocks mutex
public:
	typedef _Mutex mutex_type;

	// CONSTRUCT, ASSIGN, AND DESTROY
	unique_lock() noexcept
		: _Pmtx(nullptr), _Owns(false)
		{	// default construct
		}

	explicit unique_lock(_Mutex& _Mtx)
		: _Pmtx(_STD addressof(_Mtx)), _Owns(false)
		{	// construct and lock
		_Pmtx->lock();
		_Owns = true;
		}

	unique_lock(_Mutex& _Mtx, adopt_lock_t)
		: _Pmtx(_STD addressof(_Mtx)), _Owns(true)
		{	// construct and assume already locked
		}

	unique_lock(_Mutex& _Mtx, defer_lock_t) noexcept
		: _Pmtx(_STD addressof(_Mtx)), _Owns(false)
		{	// construct but don't lock
		}

	unique_lock(_Mutex& _Mtx, try_to_lock_t)
		: _Pmtx(_STD addressof(_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(_STD addressof(_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(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock_until(_Abs_time))
		{	// construct and lock with timeout
		}

	unique_lock(_Mutex& _Mtx, const xtime *_Abs_time)
		: _Pmtx(_STD addressof(_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 = nullptr;
		_Other._Owns = false;
		}

	unique_lock& operator=(unique_lock&& _Other)
		{	// destructive copy
		if (this != _STD addressof(_Other))
			{	// different, move contents
			if (_Owns)
				_Pmtx->unlock();
			_Pmtx = _Other._Pmtx;
			_Owns = _Other._Owns;
			_Other._Pmtx = nullptr;
			_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;

	void lock()
		{	// lock the mutex
		_Validate();
		_Pmtx->lock();
		_Owns = true;
		}

	_NODISCARD bool try_lock()
		{	// try to lock the mutex
		_Validate();
		_Owns = _Pmtx->try_lock();
		return (_Owns);
		}

	template<class _Rep,
		class _Period>
		_NODISCARD 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>
		_NODISCARD 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);
		}

	_NODISCARD 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(system_error(
				_STD make_error_code(errc::operation_not_permitted)));

		_Pmtx->unlock();
		_Owns = false;
		}

	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 = nullptr;
		_Owns = false;
		return (_Res);
		}

	_NODISCARD 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);
		}

	_NODISCARD _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(system_error(
				_STD make_error_code(errc::operation_not_permitted)));

		if (_Owns)
			_THROW(system_error(
				_STD make_error_code(errc::resource_deadlock_would_occur)));
		}
	};

类属性和接口说明
1、对模板参数_Mutex对象进行了托管,并增加一个bool值(加锁的标记)。
2、可支持多种方式(空、构造内lock、第二参数为三种锁标记、接受超时时间类参数、move语义…)的构造,析构内根据加锁标记调用unlock()。
3、创建类模板对象

std::unique_lock<std::mutex> un_lock(my_mutex);

4、可直接取代lock_guard类模板,非常灵活,更复杂,效率略低。
5、无拷贝、拷贝赋值语义,但有move语义
6、接口说明

void lock();//上锁
void unlock();//解锁
bool try_lock();//尝试给互斥量加锁,返回值为是否拿到锁,该函数不阻塞
void swap(unique_lock& _Other) noexcept;//交换锁
_Mutex *release() noexcept;//返回它所管理的mutex对象指针,并释放所有权;接受返回值对象责任接管,并负责解锁
bool owns_lock() const noexcept;//判断是否拿到了锁
explicit operator bool() const noexcept;//判断是否拿到了锁
bool try_lock_for();		//可接受超时参数,返回是否拿到了锁
bool try_lock_until();	//可接受超时参数,返回是否拿到了锁

6.call_once函数模板

在多线程中保证第二参数(可调用对象:函数、lambda表达式等)只被执行一次
std::once_flag m_flag; //搭配使用
std::call_once(m_flag, CreateInstance);
使用示例请见:单例模式singleton->三.4:https://blog.csdn.net/qq_43148810/article/details/104957327

如有错误或不足欢迎评论指出!创作不易,转载请注明出处。如有帮助,记得点赞关注哦(⊙o⊙)
更多内容请关注个人博客:https://blog.csdn.net/qq_43148810

你可能感兴趣的:(并发编程,锁,多线程,mutex,并发编程,同步)