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