一、锁的作用
在多线程的操作系统中,经常会出现多个线程同时访问同一块资源的情况,比如一个线程正在对一个变量进行读操作,另一个线程也同时在对这个变量进行写的操作,则会出现逻辑混乱,甚至程序崩溃。为了保护共享资源(变量或者代码)不被并发访问,需要使用锁对共享资源的访问进行控制。
二、互斥锁
互斥锁属于线程锁的一种,是一种简单的加锁方法,互斥锁只有两种状态,即上锁和解锁。
三、互斥锁的实现
1. 头文件:#include <mutex>
2. 创建类对象:std::mutex g_Mutex;
3. 互斥锁的两种实现方法
(1)方法一:lock()和unlock()
通过调用mutex的类成员函数lock()加锁,unlock()解锁。在需要锁的代码块前后加上lock()和unlock()即可。需要注意的是,加锁和解锁,必须成对使用,否则会出现死锁现象。代码示例如下:
#include <mutex>
std::mutex g_Mutex;
void func()
{
{
g_Mutex.lock(); // 上锁
// 被锁的内容
g_Mutex.unlock(); // 解锁
}
}
注:容易出现的问题:一旦程序在以上被锁的内容中退出,却没有解锁,会出现死锁问题。
(2)方法二:使用lock_guard
lock_guard其实就是一个类,它在构造函数中加锁,在析构函数中解锁,lock_guard的源码如下:
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) noexcept : _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的便捷之处在于,在创建lock_guard对象的时候就上锁了,在锁的生命周期结束的时候,便会自动解锁,从而避免了忘记解锁的情况。这里有些类似于智能指针。代码示例如下:
#include <mutex>
std::mutex g_Mutex;
void func()
{
// lock_guard在作用域内不可拷贝构造
{
std::lock_guard<std::mutex> lock(g_Mutex);
// 被锁的内容
}
}