C++多线程编程:其六、unique_lock的使用

一、异常导致没有解锁

mutex对象需要手动解锁。但是如果在解锁之前抛出来异常,就会导致解锁逻辑没有执行。当前线程就会一直占有互斥量,其它线程就一直无法得到互斥量,就无法执行,看代码:

#include 
#include 
#include 
#include 

std::mutex mtx;

void print_event(int x)
{
    if(x%2==0)
        std::cout << x << " is even\n";
    else
        throw (std::logic_error("not even"));

}

void print_thread_id(int id)
{
    try{
        mtx.lock();
        print_event(id);
        mtx.unlock();
    }
    catch(std::logic_error&)
    {
        std::cout << "[exception caught]\n";
    }
}

int main(int argc,char **argv)
{
    std::thread threads[10];
    for(int i=0;i<10;i++)
    {
        threads[i]=std::thread(print_thread_id,i+1);
    }
    for (auto& th : threads) th.join();
    return 0;

}

执行后会发现程序被卡主,因为发生了死锁。

二、基于RAII思想的unique_lock

unique_lock在构造的时候传入mutex变量,对mutex变量加锁。在析构的时候对这个mutex变量解锁,从而实现了自动解锁。print_thread_id函数的代码可以替换为::

void print_thread_id(int id)
{
    try{
        std::unique_lock<std::mutex> ul(mtx);
        print_event(id);
    }
    catch(std::logic_error&)
    {
        std::cout << "[exception caught]\n";
    }
}

输出结果:

[exception caught]
[exception caught]
2 is even
4 is even
[exception caught]
6 is even
[exception caught]
8 is even
[exception caught]
10 is even

死锁解除。

三、unique_lock和lock_guard的区别

1、unique_lock与lock_guard都能实现自动加锁和解锁,但是前者更加灵活,能实现更多的功能。
2、unique_lock可以进行临时解锁和再上锁,如在构造对象之后使用lck.unlock()就可以进行解锁, lck.lock()进行上锁,而不必等到析构时自动解锁。lock_guard是不支持手动释放的。
3、一般来说,使用unique_lock比较多,除非追求极致的性能才会考虑使用lock_guard。

你可能感兴趣的:(c++,开发语言)