如何使用类似 std::vector<std::mutex> 的东西?

How can I use something like std::vector<std::mutex>?

本文关键字:std gt mutex 何使用 vector lt      更新时间:2023-10-16

我有大量的对象,但可能会发生变化,这些对象被并发写入。我想用互斥来保护这种访问。为此,我认为我使用了std::vector<std::mutex>,但这不起作用,因为std::mutex没有复制或移动构造函数,而std::vector::resize()需要它。

这个难题的建议解决方案是什么?

编辑:是否所有C++随机访问容器都需要复制或移动构造函数来重新调整大小?std::deque有帮助吗?

再次编辑

首先,感谢您的想法。我对避免缄默和/或将缄默转移到对象中的解决方案不感兴趣(我不透露细节/原因(。因此,考虑到我想要一个可调整数量的互斥量的问题(其中,当没有互斥量被锁定时,保证会发生调整(,那么似乎有几种解决方案。

1我可以使用固定数量的倍数,并使用哈希函数从对象映射到倍数(如Oblivous上尉的回答(。这将导致冲突,但如果多个线程的数量远大于线程的数量,但仍然小于对象的数量,则冲突的数量应该很小。

2我可以定义一个包装类(如ComicansMS的答案(,例如

struct mutex_wrapper : std::mutex
{
  mutex_wrapper() = default;
  mutex_wrapper(mutex_wrapper const&) noexcept : std::mutex() {}
  bool operator==(mutex_wrapper const&other) noexcept { return this==&other; }
};

并使用CCD_ 4。

3我可以使用std::unique_ptr<std::mutex>来管理单个互斥(如Matthias的回答(。这种方法的问题在于,每个互斥对象都是在堆上单独分配和取消分配的。因此,我更喜欢

4std::unique_ptr<std::mutex[]> mutices( new std::mutex[n_mutex] );

当最初分配了一定数量的多重数CCD_ 7时。如果以后发现这个数字不够,我只是

if(need_mutex > n_mutex) {
  mutices.reset( new std::mutex[need_mutex] );
  n_mutex = need_mutex;
}

那么我应该使用这些(1,2,4(中的哪一个呢?

vector要求值是可移动的,以便在值增长时保持连续的值数组。你可以创建一个包含互斥的向量,但你不能做任何可能需要调整它大小的事情

其他容器没有这个要求;只要在构造过程中或通过使用emplace()resize()在适当的位置构造互斥,deque[forward_]list都应该工作。insert()push_back()等功能将不起作用。

或者,您可以添加一个额外的间接级别并存储unique_ptr;但你在另一个答案中的评论表明,你认为动态分配的额外成本是不可接受的。

如果你想创建一个特定的长度:

std::vector<std::mutex> mutexes;
...
size_t count = 4;
std::vector<std::mutex> list(count);
mutexes.swap(list);

您可以使用std::unique_ptr<std::mutex>而不是std::mutexunique_ptr是可移动的。

我建议使用固定的互斥池。保留一个固定的std::mutex数组,并根据对象的地址选择要锁定的数组,就像使用哈希表一样。

std::array<std::mutex, 32> mutexes;
std::mutex &m = mutexes[hashof(objectPtr) % mutexes.size()];
m.lock();

hashof函数可以是将指针值移动几位的简单函数。这样,您只需要初始化互斥对象一次,就可以避免调整向量大小的复制。

如果效率是一个问题,我假设您只有非常小的数据结构,这些数据结构会经常更改。因此,最好使用Atomic Compare And Swap(和其他原子操作(,而不是使用互斥,特别是std::atomic_compare_exchange_strong

当我想要一个classstructstd::vector,每个都有自己的std::mutex时,有时我会使用与第二个选项类似的解决方案。当然,这有点乏味,因为我自己编写复制/移动/赋值运算符。

struct MyStruct {
  MyStruct() : value1(0), value2(0) {}
  MyStruct(const MyStruct& other) {
    std::lock_guard<std::mutex> l(other.mutex);
    value1 = other.value1;
    value2 = other.value2;
  }
  MyStruct(MyStruct&& other) {
    std::lock_guard<std::mutex> l(other.mutex);
    value1 = std::exchange(other.value1, 0);
    value2 = std::exchange(other.value2, 0);
  }
  MyStruct& operator=(MyStruct&& other) {
    std::lock_guard<std::mutex> l1(this->mutex), l2(other.mutex);
    std::swap(value1, other.value1);
    std::swap(value2, other.value2);
    return *this;
  }
  MyStruct& operator=(const MyStruct& other) {
    // you get the idea
  }
  int value1;
  double value2;
  mutable std::mutex mutex;
};

你不需要";移动";即CCD_ 26。你只需要把它锁上;移动";其他一切。

将每个互斥对象声明为指针怎么样?

std::vector<std::mutex *> my_mutexes(10)
//Initialize mutexes
for(int i=0;i<10;++i) my_mutexes[i] = new std::mutex();
//Release mutexes
for(int i=0;i<10;++i) delete my_mutexes[i];