码迷,mamicode.com
首页 > 编程语言 > 详细

[C++11 并发编程] 08 - Mutex std::unique_lock

时间:2015-08-21 23:26:36      阅读:5656      评论:0      收藏:0      [点我收藏+]

标签:c++11   concurrency   unique_lock   

相对于std::lock_guard来说,std::unique_lock更加灵活,std::unique_lock不拥有与其关联的mutex。构造函数的第二个参数可以指定为std::defer_lock,这样表示在构造unique_lock时,传入的mutex保持unlock状态。然后通过调用std::unique_lock对象的lock()方法或者将将std::unique_lock对象传入std::lock()方法来锁定mutex。

#include <mutex>

class some_big_object
{};

void swap(some_big_object& lhs,some_big_object& rhs)
{}

class X
{
private:
    some_big_object some_detail;
    mutable std::mutex m;
public:
    X(some_big_object const& sd):some_detail(sd){}

    friend void swap(X& lhs, X& rhs)
    {
        if(&lhs==&rhs)
            return;
        // 构造unique_lock,保持mutex为unlocked状态
        std::unique_lock<std::mutex> lock_a(lhs.m,std::defer_lock);
        std::unique_lock<std::mutex> lock_b(rhs.m,std::defer_lock);
        // lock mutex
        std::lock(lock_a,lock_b);
        swap(lhs.some_detail,rhs.some_detail);
    }
};

int main()
{}

std::unique_lock比std::lock_guard需要更大的空间,因为它需要存储它所关联的mutex是否被锁定,如果被锁定,在析构该std::unique_lock时,就需要unlock它所关联的mutex。std::unique_lock的性能也比std::lock_guard稍差,因为在lock或unlock mutex时,还需要更新mutex是否锁定的标志。大多数情况下,推荐使用std::lock_guard但是如果需要更多的灵活性,比如上面这个例子,或者需要在代码之间传递lock的所有权,这可以使用std::unique_lock。

std::unique_lock并不拥有与其关联的mutex,mutex的所有权可以在不同的实例之间进行传递。比如我们期望提供一个函数,在锁定mutex后,将mutex的所有权返回给调用者,这样可以让调用者在mutex的保护下,执行更多额外的操作。如下所示的get_lock()函数,锁定mutex后,执行prepare_data(),然后将mutex返回给调用者,调用者将mutex传入其自己的局部变量std::unique_lock,然后mutex的保护下调用do_something()。当process_data()退出时,会自动unlock这个mutex。

std::unique_lock<std::mutex> get_lock()
{
    extern std::mutex some_mutex;
    std::unique_lock<std::mutex> lk(some_mutex);
    prepare_data();
    return lk;
}
void process_data()
{
    std::unique_lock<std::mutex> lk(get_lock());
    do_something();
}
std::unique_lock的灵活性还在于我们可以主动的调用unlock()方法来释放mutex,因为锁的时间越长,越会影响程序的性能,在一些特殊情况下,提前释放mutex可以提高程序执行的效率。

此外,我们还需要注意锁的粒度,如果有多个线程等待相同的资源,而某个线程长时间的持有mutex,就会增加其它线程的等待时间。我们要尽量保证只对共享数据加锁,在锁定范围之外对数据进行处理。不要在锁定mutex的情况下执行I/O操作,因为I/O操作是很慢的。可以在必要的情况下调用std::unique_lock的unlock()操作来释放mutex,在需要时,再调用lock()来锁定mutex。

void get_and_process_data()
{
    std::unique_lock<std::mutex> my_lock(the_mutex);
    some_class data_to_process=get_next_data_chunk();
    my_lock.unlock(); // 在process中不需要锁定mutex
    result_type result=process(data_to_process);
    my_lock.lock(); // 在写操作前再次锁定mutex
    write_result(data_to_process,result);
} 

在swap实例中,我们锁定了两个对象的mutex再进行比较交换操作。假设我们要比较两个对象,而复制对象的代价很小,则可以考虑减小mutex保护的范围和时间,在锁定mutex时,对对象进行复制,释放mutex后,用两个复制的对象来进行比较操作。

#include <mutex>
class Y
{
private:
    int some_detail;
    mutable std::mutex m;

    // 返回对象的拷贝
    int get_detail() const
    {
        // 保护对象
        std::lock_guard<std::mutex> lock_a(m);
        return some_detail;
    }
public:
    Y(int sd):some_detail(sd){}

    friend bool operator==(Y const& lhs, Y const& rhs)
    {
        if(&lhs==&rhs)
            return true;
        // 获取要比较对象的拷贝
        int const lhs_value=lhs.get_detail();
        int const rhs_value=rhs.get_detail();
        // 比较对象的拷贝
        return lhs_value==rhs_value;
    }
};

int main()
{}

采用这种方法,虽然减小了mutex锁定的范围和时间,但是却改变了比较的语义。因为这种实现方法只能保证某一时刻读取的lhs.some_detail和另一时刻读取的rhs.some_detail相等,但是在读取lhs_value和读取lrh_value之间存在race condition,它们的值可能已经被修改了。




版权声明:本文为博主原创文章,未经博主允许不得转载。

[C++11 并发编程] 08 - Mutex std::unique_lock

标签:c++11   concurrency   unique_lock   

原文地址:http://blog.csdn.net/yamingwu/article/details/47841689

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!