如何 vector::emplace_back 有 shared_mutex 的 class?

How to vector::emplace_back a class that has a shared_mutex?

我怎样才能 emplace_back 有一个 shared_mutex 的 class?

看起来 shared_mutex 让 class 不再是 MoveConstructible/CopyConstructible。

编译我的代码得到 result type must be constructible from value type of input range。还有 no matching function for call to construct_at(class_with_lock*&, class_with_lock).

最小示例:

//compile me with g++ -std=c++2a lock.cpp -o lock
//tested under linux with gcc 11.1
#include <vector>
#include <shared_mutex>

class class_with_lock {
    public:
    class_with_lock(){}
    
    void do_lock(void) {
        std::shared_lock lock(my_lock);
    }
    
    private:
    mutable std::shared_mutex my_lock;
};
    
class my_vector_class {
    public:
    my_vector_class() {
        my_vector.emplace_back(class_with_lock{});
    }
    private:
    std::vector<class_with_lock> my_vector;
};

int main(void) {
    my_vector_class my_class;
    return 0;
}

std::shared_mutex is not copyable,但是 std::vector 需要 copyable/movable 个对象。

您不能使用带有 std::shared_mutex 的向量或带有 std::shared_mutex 的对象,除非您有一个用户定义的 copy/move 构造函数,该构造函数可以为这些对象执行任何操作 copied/moved, 但没有相应的 std::shared_mutex.

您还可以切换到可以支持非copyable/movable 对象的不同容器,例如std::list

互斥体通常对 copy/move 不安全;它根本无法编译,并且操作本身没有意义。

如果你有一个带有互斥量的类型,而你是 copy/moving 它,你几乎可以肯定做错了什么。

class 带锁的 vector 有臭味,不要这样做。

语义开始表现得非常非常奇怪。

如果一个对象有一个互斥体,那么这个互斥体的身份比它的值更重要。您没有锁定该互斥锁的 value,而是锁定了该互斥锁的 identity。身份不能被复制,所以带有互斥量的 classes 要么不能被复制,要么进行混合语义 copy/move 操作,其中 part 的状态是 copied/moved,其余的不是。

混合语义类型是疯子,而且难以推理。这与为什么 struct 不应同时包含引用和值的原因相同,但更糟糕的是。

std::vector 专为常规或半常规值类型而设计。当你放置时,它可能会分配一个新的缓冲区并将元素移到它上面。

将带有互斥量的类型放入其中意味着常规或半常规语义在它们上面是不连贯的;它不能适用于整个州。

有可能让它发挥作用,但这不是一个好的计划。您要做的是编写不 duplicate/move 互斥锁状态的不连贯的 copy/move 构造函数和赋值操作,并对其余状态做一些事情。那里可能涉及一些锁定,但即使那样也会变得令人讨厌和棘手。

(基于互斥锁的线程无法组合;现在您有不连贯的值,这些值由被复制的多个锁保护。它会以糟糕的方式结束。)

(这里的不连贯是指混合语义;对象的一部分(互斥体)不是copied/moved,同时值是。连贯语义意味着“class都遵循相同的语义”,不连贯意味着它没有连贯的语义。)

最不疯狂的选择涉及在你做赋值之前使用std::lock锁定source/destination对象的互斥量,并且在构建时不要费心锁定目标(只是源)因为在构造对象时,任何其他线程都不应访问该对象。

我的意思是,我已经做到了。

这不是一个好计划。


一个稍微不那么糟糕的计划是拥有一个指向您的 class 的唯一指针向量。在这里,对象的身份不是一次性的;该向量包含 unique_ptrs(它们是半规则的,因此可以合理地移动),这又指向具有稳定身份的对象。

其中的互斥体在某种程度上是合理的。拥有此类互斥体的动态向量通常是一个不好的迹象(代码味道不好),但这并不完全是精神错乱。