我们知道c++11 提供了很多的类模板供我们使用,其中就有 lock_guard,这个用法也很简单,像这样:
std::mutex m_mutex;
std::lock_guard<std::mutex> guard(m_mutex);
mutex 的 lock 和 unlock 完全是自动的,无需我们手动操作。因为模板类 lock_guard 在构造函数里进行了 lock,在析构函数里进行了 unlock。我们可以看源码实现:
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 具有同样功能的还有一个:unique_lock。同样的,unique_lock 也是可以自动锁定和解锁,但其多了几个接口,且构造函数里也多了一些参数。
1,和 lock_guard 一样的功能,如:
#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>
std::mutex mtx;
void funcProc()
{
std::unique_lock<std::mutex> guard(mtx);
int count = 5;
while(count-- > 0)
{
printf("in new thread\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main()
{
std::thread t1(funcProc);
//先让子线程跑起来再去获取锁
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf("main thread begin to get mtx\n");
{
std::unique_lock<std::mutex> guard(mtx);
int count = 5;
while(count -- > 0)
{
printf("main thread already get mtx\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
t1.join();
return 0;
}
很简单,子线程先获取锁,执行完后 main 线程获取锁,这里完全靠自动锁定和解锁。
2,构造函数里参数 std::defer_lock
当我们在构造函数里添加参数 std::defer_lock 时,此时构造函数里并没有调用锁的 lock(),即之后需要我们手动去调用 lock() 进行锁定,如:
#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>
std::mutex mtx;
void funcProc()
{
//加了defer_lock, 构造函数里不会进行lock
std::unique_lock<std::mutex> guard(mtx, std::defer_lock);
int count = 5;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
//这里尝试锁定,但此锁已经被 main 线程锁定了
guard.lock();
while(count-- > 0)
{
printf("in new thread\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main()
{
std::thread t1(funcProc);
printf("main thread begin to get mtx\n");
{
std::unique_lock<std::mutex> guard(mtx);
int count = 5;
while(count -- > 0)
{
printf("main thread already get mtx\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
//这里已经解锁,子线程可以获取锁了
printf("main thread unlock\n");
t1.join();
return 0;
}
我们看源码里,带defer_lock参数的构造函数是这样的:
unique_lock(mutex_type& __m, defer_lock_t) noexcept
: _M_device(&__m), _M_owns(false)
{ }
3,lock() 函数
相比 lock_guard ,unique_lock 可以进行手动 lock,但如果是已经lock的情况下再lock时,则会抛出异常,如:
#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>
std::mutex mtx;
void funcProc()
{
std::unique_lock<std::mutex> guard(mtx);
int count = 5;
//当前线程已经锁定时,再调用 lock() 则会抛出异常
guard.lock();
while(count-- > 0)
{
printf("in new thread\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main()
{
std::thread t1(funcProc);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf("main thread begin to get mtx\n");
{
std::unique_lock<std::mutex> guard(mtx);
int count = 5;
while(count -- > 0)
{
printf("main thread already get mtx\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
//这里已经解锁,子线程可以获取锁了
printf("main thread unlock\n");
t1.join();
return 0;
}
4,unlock 函数
在没有 lock 的情况下进行 unlock 也会抛出异常,如:
#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>
std::mutex mtx;
void funcProc()
{
std::unique_lock<std::mutex> guard(mtx, std::defer_lock);
int count = 5;
//没有锁定的情况下解锁,则抛出异常
guard.unlock();
while(count-- > 0)
{
printf("in new thread\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main()
{
std::thread t1(funcProc);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf("main thread begin to get mtx\n");
{
std::unique_lock<std::mutex> guard(mtx);
int count = 5;
while(count -- > 0)
{
printf("main thread already get mtx\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
//这里已经解锁,子线程可以获取锁了
printf("main thread unlock\n");
t1.join();
return 0;
}
5,release 函数
注意这个 release 函数,它并不会解锁,所以这个函数慎用,否则就是死锁,如:
#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>
std::mutex mtx;
void funcProc()
{
std::unique_lock<std::mutex> guard(mtx);
int count = 5;
while(count-- > 0)
{
printf("in new thread\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
if(count == 2)
{
printf("in new thread, release mtx\n");
//release()并没有解锁,main 线程则会一直在等锁,若要解锁则:guard.release()->unlock()
guard.release();
}
}
}
int main()
{
std::thread t1(funcProc);
//先让子线程跑起来再去获取锁
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
printf("main thread begin to get mtx\n");
{
std::unique_lock<std::mutex> guard(mtx);
int count = 5;
while(count -- > 0)
{
printf("main thread already get mtx\n");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
t1.join();
return 0;
}
我们看 release 的源码 :
mutex_type*
release() noexcept
{
mutex_type* __ret = _M_device;
_M_device = 0;
_M_owns = false;
return __ret;
}
所以我们要解锁的话,得这样调用:guard.release()->unlock()。
其他函数这里就不讲了。