【问题标题】:Mutex in operator []运算符 [] 中的互斥锁
【发布时间】:2017-11-27 04:20:51
【问题描述】:

我想做这样的事情:

class BlockingVector{
protected:
    std::vector<int> m_vector;
    std::mutex m_mutex;
public:
    int & operator [](size_t p_index){
        std::lock_guard<std::mutex> lock(m_mutex);
        return m_vector[p_index];
    }
};

我知道,这是完全错误的。 有没有办法用互斥锁重载运算符 []?

正如 Nikos C 所说:它返回对元素的引用,而不是副本。因此使用该元素不是线程安全的。

【问题讨论】:

  • 你想完成什么?
  • 为什么会出错?您对代码有什么问题?你如何使用它?请read about how to ask good questions,并学习如何创建Minimal, Complete, and Verifiable Example
  • 是什么让你认为这是“完全错误的”?
  • @Yuushi 它返回对元素的引用,而不是副本。因此使用该元素不是线程安全的。
  • @HoànTrần 在不知道您的限制、要求和允许的情况下:不。

标签: c++ operator-overloading mutex


【解决方案1】:

你可以定义一个'locked_reference'帮助类,在其构造函数中获取锁并在析构函数中释放,并将其用作operator[]的返回值:

template<class T> class BlockingVector{
private:
    std::vector<T> m_vector;
    std::recursive_mutex m_mutex;
    class locked_ref {
        T &ref
        lock_guard<std::recursive_mutex> lock;
    public:
        locked_ref(T &r, std::recursive_mutex &m) : ref(r), lock(m) {}
        locked_ref(locked_ref &&) = default;
        const T &operator=(const T &v) const { return ref = v; }
        operator T() const { return ref; }
    }; 
public:
    locked_ref operator [](size_t p_index){
        return locked_ref(m_vector[p_index], m_mutex); }
};

你需要一个 recursive_mutex,因为一个线程需要多次锁定它来评估类似的东西:

v[i] = v[j] + v[k];

如果两个线程同时对两个向量进行操作,则存在死锁的危险。

【讨论】:

    【解决方案2】:

    没有。调用者需要获取锁,因为它获取的是对数据的引用而不是副本。您不能以某种方式为所有使用它的代码引用int 线程安全。

    如果您需要对数据进行线程安全操作并且不希望调用者负责,您有两种选择:a) 在您的类的 API 中添加该操作,或者 b) 实现一个包装器类型int 参考。

    正确实现包装器可能很复杂,因为您现在需要担心每一种可能的极端情况。可以复制吗?活动?都以线程安全的方式?

    所以我会选择将操作添加为 API。喜欢:

    class BlockingVector{
        // ...
    
        void setVal(size_t index, int newVal)
        {
            std::lock_guard<std::mutex> lock(m_mutex);
            m_vector[index] = newVal;
        }
    
        // Change: return copy, not reference.
        int operator [](size_t p_index) const {
            std::lock_guard<std::mutex> lock(m_mutex);
            return m_vector[p_index];
        }
    };
    

    但是,这可能是一个令人困惑的 API。所以我建议不要重载operator[],而是使用普通函数:

    int getVal(size_t p_index) const {
        std::lock_guard<std::mutex> lock(m_mutex);
        return m_vector[p_index];
    }
    

    请注意,为了能够在 const 函数中锁定互斥体,您需要使其可变:

    class BlockingVector{
        // ...
        mutable std::mutex m_mutex;
    

    您应该阅读本文以获取有关互斥锁和const 函数的更多信息:

    Should mutexes be mutable?

    【讨论】:

    • 有点奇怪。使用互斥锁时函数可能是 const( std::lock_guard<:mutex> lock(m_mutex);) ?
    • @HoànTrần 通常将互斥锁标记为mutable。在这种情况下,您需要使用mutable std::mutex m_mutex; 声明互斥锁。然后你甚至可以在const 函数中锁定它。但是,我建议您阅读以下内容:stackoverflow.com/questions/4127333/…
    猜你喜欢
    • 2016-07-26
    • 2018-05-23
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 2012-06-05
    • 2010-09-16
    • 2010-12-17
    • 1970-01-01
    相关资源
    最近更新 更多