多线程,lock_guard和unique_lock的使用

     ~~~~     之前使用锁的时候,要自动释放,就自己写了一个类,通过构造和析构函数自动释放锁,后来发现了std::lock_guard,然后就没有使用自己写的自动释放类了。
     ~~~~     今天又发现了std::unique_lock,功能比std::lock_guard要多,同时也有自动加锁和解锁,功能更加灵活,但是相应地性能没有std::lock_guard要好。
先附上一个自己写的自动释放的类

class CAutoMutex{
public:
    CAutoMutex(mutex* val){
        m_mutex = val;
        if(m_mutex)
            m_mutex->lock();
    }
    ~CAutoMutex(){
        if(m_mutex)
            m_mutex->unlock();
        m_mutex = NULL;
    }

private:
    mutex *m_mutex;
};

我们可以看一下std::lock_guard的源码,好像比我写得好,用的是引用。

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

然后我们写一个std::lock_guard的例子,可以用我上面写的类直接替换哦。

#include <thread>
#include <mutex>
#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

int a = 1000;

mutex g_mutex;

//输出
void test(int val1, int val2){
    printf("thread %d run. a = %d.\n", val1, val2);
}

void thread_run(int val){
    while(true)
    {
        {
            //访问a
            if(a < 0) break;
            lock_guard<mutex> lock(g_mutex);
            test(val, a);
            a--;
        }
        this_thread::sleep_for(chrono::milliseconds(10));
    }
}

int main(){
    printf("start test.\n");
    std::vector<std::thread> ver;   //保存线程
    for(int i = 0; i < 5; i++){
        std::thread t = thread(thread_run, i);
        ver.emplace_back(std::move(t)); //保存
    }   
    //等待结束
    std::for_each(ver.begin(), ver.end(), std::mem_fn(&std::thread::join));
    
    printf("finish test.\n");
}

下面是编译,外加结果:

qilimi@qilimi-desktop:~/test$ vim test_lick_guard.cpp
qilimi@qilimi-desktop:~/test$ g++ -o test test_lick_guard.cpp -lpthread -std=c++11
qilimi@qilimi-desktop:~/test$ ./test
start test.
thread 0 run. a = 100.
thread 1 run. a = 99.
thread 2 run. a = 98.
thread 3 run. a = 97.
thread 4 run. a = 96.
thread 3 run. a = 95.
thread 2 run. a = 94.
thread 1 run. a = 93.
thread 0 run. a = 92.
thread 4 run. a = 91.
thread 3 run. a = 90.
...
thread 1 run. a = 10.
thread 3 run. a = 9.
thread 0 run. a = 8.
thread 2 run. a = 7.
thread 4 run. a = 6.
thread 3 run. a = 5.
thread 0 run. a = 4.
thread 1 run. a = 3.
thread 2 run. a = 2.
thread 4 run. a = 1.
thread 0 run. a = 0.
thread 2 run. a = -1.
thread 3 run. a = -2.
thread 4 run. a = -3.
thread 1 run. a = -4.
finish test.
qilimi@qilimi-desktop:~/test$

然后我们再来看看std::unique_lock,有哪些功能,我们先上std::unique_lock的源码:

		// CLASS TEMPLATE 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)));
		}
	};

     ~~~~     我们可以发现,如果带一个参数,效果和std::lock_guard一样的,它的带两个参数的构造函数有三个,分别为构造且默认已经锁住(带adopt_lock_t,已经加锁,这个在std::lock_guard里面也有)、构造且不加锁(带defer_lock_t,没有加锁)、构造且尝试加锁(带try_to_lock_t,尝试加锁,需要查询是否加锁成功)、延时加锁(带时间参数)’,同时多了一个变量,保存有没有加锁,可以去获取它的状态。
     ~~~~     代码很简单,大家可以自己看一看,我们就在实例一次:

#include <thread>
#include <mutex>
#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

int a = 1000;

mutex g_mutex;

//输出
void test(int val1, int val2){
    printf("thread %d run. a = %d.\n", val1, val2);
}

void thread_run(int val){
    while(true)
    {
        {
            //访问a
            if(a < 0) break;
            //尝试加锁
            unique_lock<mutex> lock(g_mutex, try_to_lock);
            //判断是否加锁
            if(lock.owns_lock()){
                test(val, a);
                a--;
            }
        }
        this_thread::sleep_for(chrono::milliseconds(10));
    }
}

int main(){
    printf("start test.\n");
    std::vector<std::thread> ver;   //保存线程
    for(int i = 0; i < 5; i++){
        std::thread t = thread(thread_run, i);
        ver.emplace_back(std::move(t)); //保存
    }   
    //等待结束
    std::for_each(ver.begin(), ver.end(), std::mem_fn(&std::thread::join));
    
    printf("finish test.\n");
}

下面是编译和输出

qilimi@qilimi-desktop:~/test$ vim test_lick_guard3.cpp
qilimi@qilimi-desktop:~/test$ g++ -o test3 test_lick_guard3.cpp -lpthread -std=c++11
qilimi@qilimi-desktop:~/test$ ./test3
start test.
thread 0 run. a = 100.
thread 0 run. a = 99.
thread 4 run. a = 98.
thread 2 run. a = 97.
thread 3 run. a = 96.
thread 1 run. a = 95.
thread 4 run. a = 94.
thread 1 run. a = 93.
thread 0 run. a = 92.
thread 1 run. a = 91.
thread 0 run. a = 90.
...
thread 2 run. a = 10.
thread 2 run. a = 9.
thread 1 run. a = 8.
thread 2 run. a = 7.
thread 0 run. a = 6.
thread 3 run. a = 5.
thread 2 run. a = 4.
thread 0 run. a = 3.
thread 4 run. a = 2.
thread 2 run. a = 1.
thread 1 run. a = 0.
finish test.

大家仔细看源码就会发现unique_lock还可以临时解锁,然后又加锁,相对功能比lock_guard还是多很多的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值