首页 > 解决方案 > 具有互斥锁缓存的类的移动构造函数的最佳实践

问题描述

我有时有这样的课程:

class HasMutexLockedCache {
private:
    SomeType m_regularData;
    mutable std::mutex m_mutex;
    mutable std::optaional<T> m_cache;
    // Etc.

public:
    const m_regularData& getData() const { return m_regularData; }
    
    const T& getExpensiveResult() const {
        std::scoped_lock lock(m_mutex);
        if (!m_cache) {
            m_cache = expensiveFunction();
        }
        return m_cache;
    }
    HasMutexLockedCache(const HasMutexLockedCache& other) 
        : m_regularData(other.m_regularData) 
    {
        std::scoped_lock lock(other.m_mutex);
        // I figure we don't have to lock this->m_mutex because
        // we are in the c'tor and so nobody else could possibly
        // be messing with m_cache.
        m_cache = other.m_cache;
    }
    HasMutexLockedCache(HasMutexLockedCache&& other)
        : m_regularData(std::move(other.m_regularData))
    {
        // What here?
    }
    HasMutexLockedCache& operator=(HasMutexLockedCache&& other) {
        m_regularData = std::move(other.m_regularData);
        // Bonus points: What here? Lock both mutexes? One? 
        // Only lock this->m_mutex depending on how we 
        // document thread safety?
    }
};

我的问题:什么进入HasMutexLockedCache(HasMutexLockedCache&& other)(同样在HasMutexLockedCache& operator=(HasMutexLockedCache&& other)?我认为我们不需要锁定other.m_mutex,因为other作为一个右值引用,我们知道没有其他人可以看到它,就像我们不必锁定this->m_mutexc' tor。但是,我想要一些指导。这里的最佳做法是什么?我们应该锁定other.m_mutex吗?

标签: c++move-semantics

解决方案


我想要一些指导。这里有哪些最佳实践?我们应该锁定 other.m_mutex 吗?

@Galik 的回答解释了如何为此实现移动构造函数,但是您应该考虑这对于您的抽象来说是否是一个安全且连贯的想法。

如果一个对象包含 a std::mutex,通常这意味着它可能在不同的时间有并发访问,这保证了这一点。如果是这种情况,那么在面对多线程时,移动语义可能会非常难以使用——因为您可能让线程 Am_cache在线程 B 访问它之前移动它的内容,从而导致它读取一个已移动的状态(这取决于正在检查的状态,可能没有明确定义)。这些类型的错误可能很难调试,甚至更难重现!

通常,如果您有这样的类型,最好在线程之间显式共享这种类型,或者通过共享生命周期 via shared_ptr,或者从外部进行某种形式的同步,这样每个线程就不会破坏性地相互干扰。


推荐阅读