c++11 unique_lock 使用

本文详细介绍了C++11中的lock_guard和unique_lock两种智能锁的使用,包括它们的自动锁定和解锁功能,以及unique_lock的特殊操作如defer_lock、lock()、unlock()和release()。通过示例代码展示了它们在多线程同步中的应用,强调了使用中的注意事项,如避免异常和死锁情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        我们知道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()。

其他函数这里就不讲了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值