std::unique_lock 移动语义

Posted

技术标签:

【中文标题】std::unique_lock 移动语义【英文标题】:std::unique_lock move semantic 【发布时间】:2020-01-15 19:12:05 【问题描述】:

请帮我检查一下我对std::unique_lock移动语义的理解是否正确。 假设我有一个函数myFunc,它需要在执行开始时获取一个互斥锁。 这个函数是从不同的函数调用的,其中一些函数,例如myWrapper1 在调用 myFunc 之前持有相同的互斥锁。 有时我需要在myFunc 的作用域退出时不释放这个锁。

typedef std::unique_lock<std::mutex> MyLock;
class Example2

    std::mutex m_data_mutex;

    MyLock&& myFunc(MyLock&& lk)
    
        bool was_lock = lk.owns_lock();
        if( !was_lock ) lk.lock();

        std::cout << "myFunc" << std::endl;

        if( !was_lock ) lk.unlock();
        return std::move(lk);
    

    void myWrapper1()
    
        MyLock lk(m_data_mutex);
        std::cout << "1" << std::endl;
        lk = myFunc(std::move(lk));
        std::cout << "2" << std::endl;
    

    void myWrapper2()
    
        MyLock lk(m_data_mutex);
        std::cout << "1" << std::endl;
        lk.unlock();
        lk = myFunc(std::move(lk));
        std::cout << "2" << std::endl;
        lk.lock();
        std::cout << "3" << std::endl;
    
;

所以问题是:

    对于myWrapper1,保证MyLock 只会在myWrapper1 作用域结束时被释放,不是吗? 我是否使用正确的习语来解决这个问题?

【问题讨论】:

1.是的,2. 不。从技术上讲,您只是将 lk 分配给自己,这是没有意义的。 @osuka_ std::move 移动是错觉。 std::move 只需要对对象的右值引用。由于引用也从 myFunc 返回,因此不会发生所有权更改。 @OlhaPavliuk 最好 void myFunc(MyLock& lk) 并在传递 lk 时删除 std::move 只会让人感到困惑。 @OlhaPavliuk 这两个例子都没有发生任何事情,因为第二个锁永远不会被实例化。没有调用构造函数;只有当锁是“通过副本”(即,不是通过引用)传递时,所有权才会转移 - 然后,将调用 move c'tor。 @osuka_ 谢谢,我知道了! 【参考方案1】:

你有什么理由不能做这样的事情吗? IMO,这比在所有者之间移动锁要干净得多。

std::mutex my_lock;

void myFunc_locked() 
    ...do something that requires my_lock to be locked...


void myFunc() 
    std::lock_guard<std::mutex> guard(my_lock);
    myFunc_locked();


void myWrapper1() 
    std::lock_guard<std::mutex> guard(my_lock);
    ...do something else that requires my_lock to be locked...
    myFunc_locked();


void myWrapper2() 
    ...
    myFunc();


或者,按照@Nevin 的建议进行操作。也许更干净:

std::mutex my_lock;

void myFunc(std::lock_guard<std::mutex>> const& guard) 
    ...do something that requires my_lock to be locked...


void myFunc() 
    std::lock_guard<std::mutex> guard(my_lock);
    myFunc(guard);


void myWrapper1() 
    std::lock_guard<std::mutex> guard(my_lock);
    ...do something else that requires my_lock to be locked...
    myFunc(guard);


void myWrapper2() 
    ...
    myFunc();

【讨论】:

谢谢,这是一个好方法。但是有时很难看出在调用myFunc 之前是否没有获得相同的锁,并且可能会发生死锁。其实,我是在陷入僵局之后才开始探索这个问题的。

以上是关于std::unique_lock 移动语义的主要内容,如果未能解决你的问题,请参考以下文章

c++ 移动语义及使用(续)

std::vector 和移动语义

可变引用是不是具有移动语义?

尝试使用移动语义创建线程保护

一文入魂:再也不用担心我不懂C++移动语义了!

详解 移动端语义化标签