简体   繁体   English

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

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

I have a large, but potentially varying, number of objects which are concurrently written into.我有大量但可能不同的对象同时写入。 I want to protect that access with mutexes.我想用互斥锁保护该访问。 To that end, I thought I use a std::vector<std::mutex> , but this doesn't work, since std::mutex has no copy or move constructor, while std::vector::resize() requires that.为此,我想我使用std::vector<std::mutex> ,但这不起作用,因为std::mutex没有复制或移动构造函数,而std::vector::resize()需要那。

What is the recommended solution to this conundrum?这个难题的推荐解决方案是什么?

edit : Do all C++ random-access containers require copy or move constructors for re-sizing?编辑:是否所有 C++ 随机访问容器都需要复制或移动构造函数来重新调整大小? Would std::deque help? std::deque 会有帮助吗?

edit again再次编辑

First, thanks for all your thoughts.首先,感谢您的所有想法。 I'm not interested in solutions that avoid mutices and/or move them into the objects (I refrain from giving details/reasons).我对避免静音和/或将它们移动到对象中的解决方案不感兴趣(我不提供细节/原因)。 So given the problem that I want a adjustable number of mutices (where the adjustment is guaranteed to occur when no mutex is locked), then there appear to be several solutions.因此,考虑到我想要可调数量的互斥锁(在没有互斥锁锁定时保证进行调整)的问题,那么似乎有几种解决方案。

1 I could use a fixed number of mutices and use a hash-function to map from objects to mutices (as in Captain Oblivous's answer). 1我可以使用固定数量的 mutic,并使用哈希函数从对象映射到 mutic(如 Oblivous 上尉的回答)。 This will result in collisions, but the number of collisions should be small if the number of mutices is much larger than the number of threads, but still smaller than the number of objects.这会导致冲突,但是如果mutices的数量远大于线程的数量,但仍然小于对象的数量,那么冲突的数量应该很小。

2 I could define a wrapper class (as in ComicSansMS's answer), eg 2我可以定义一个包装类(如 ComicSansMS 的回答),例如

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; }
};

and use a std::vector<mutex_wrapper> .并使用std::vector<mutex_wrapper>

3 I could use std::unique_ptr<std::mutex> to manage individual mutexes (as in Matthias's answer). 3我可以使用std::unique_ptr<std::mutex>来管理单个互斥锁(如 Matthias 的回答)。 The problem with this approach is that each mutex is individually allocated and de-allocated on the heap.这种方法的问题在于,每个互斥体都是在堆上单独分配和取消分配的。 Therefore, I prefer因此,我更喜欢

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

when a certain number n_mutex of mutices is allocated initially.当最初分配一定数量的n_mutex互斥体时。 Should this number later be found insufficient, I simply如果以后发现这个数字不够,我只是

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

So which of these (1,2,4) should I use?那么我应该使用这些 (1,2,4) 中的哪一个?

vector requires that the values are movable, in order to maintain a contiguous array of values as it grows. vector要求值是可移动的,以便在增长时保持连续的值数组。 You could create a vector containing mutexes, but you couldn't do anything that might need to resize it.您可以创建一个包含互斥体的向量,但您无法执行任何可能需要调整其大小的操作。

Other containers don't have that requirement;其他容器没有这个要求; either deque or [forward_]list should work, as long as you construct the mutexes in place either during construction, or by using emplace() or resize() .只要在构造过程中或使用emplace()resize()构造互斥锁, deque[forward_]list都应该工作。 Functions such as insert() and push_back() will not work.诸如insert()push_back()函数将不起作用。

Alternatively, you could add an extra level of indirection and store unique_ptr ;或者,您可以添加额外的间接级别并存储unique_ptr but your comment in another answer indicates that you believe the extra cost of dynamic allocation to be unacceptable.但是您在另一个答案中的评论表明您认为动态分配的额外成本是不可接受的。

If you want to create a certain length:如果要创建某个长度:

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

mutexes.swap(list);

You could use std::unique_ptr<std::mutex> instead of std::mutex .您可以使用std::unique_ptr<std::mutex>而不是std::mutex unique_ptr s are movable. unique_ptr是可移动的。

I suggest using a fixed mutex pool.我建议使用固定的互斥池。 Keep a fixed array of std::mutex and select which one to lock based on the address of the object like you might do with a hash table.保留一个固定的std::mutex数组,并根据对象的地址选择要锁定的数组,就像使用哈希表一样。

std::array<std::mutex, 32> mutexes;

std::mutex &m = mutexes[hashof(objectPtr) % mutexes.size()];

m.lock();

The hashof function could be something simple that shifts the pointer value over a few bits. hashof函数可能很简单,将指针值移动几位。 This way you only have to initialize the mutexes once and you avoid the copy of resizing the vector.这样您只需初始化一次互斥体,就可以避免调整向量大小的副本。

If efficiency is such a problem, I assume that you have only very small data structures which are changed very often.如果效率是一个问题,我假设您只有非常小的数据结构,并且经常更改。 It is then probably better to use Atomic Compare And Swap (and other atomic operations) instead of using mutexes, specifically std::atomic_compare_exchange_strong那么最好使用原子比较和交换(和其他原子操作)而不是使用互斥锁,特别是std::atomic_compare_exchange_strong

I'll sometimes use a solution along the lines of your 2nd option when I want a std::vector of class es or struct s that each have their own std::mutex .当我想要一个std::vector class es 或struct s 时,我有时会使用你的第二个选项的解决方案,每个类都有自己的std::mutex Of course, it is a bit tedious as I write my own copy/move/assignment operators.当然,当我编写自己的复制/移动/赋值运算符时,这有点乏味。

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;
};

You don't need to "move" the std::mutex .您不需要“移动” std::mutex You just need to hold a lock on it while you "move" everything else.您只需要在“移动”其他所有东西时锁定它即可。

How about declaring each mutex as a pointer?如何将每个互斥体声明为指针?

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];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何安全地使用 std::vector 和互斥锁? - How can I use std::vector safely with mutex? 我如何为 std::variant 编写类似开关的代码? - How can I code something like a switch for std::variant? 如何对 std::vector 元素中的对象使用 shared_mutex - How to use shared_mutex for objects in std::vector elements 如何使用像 `std::basic_istream 这样的东西<std::byte> `</std::byte> - How to use something like `std::basic_istream<std::byte>` 我可以在 std::move 之后重复使用像 std::vector 这样的复杂 class 吗? - Can I re-use a complex class like std::vector after std::move? 我可以使用std :: generate来获取std :: array的向量 <T, 2> ? - Can I use std::generate to get a vector of std::array<T, 2>? 实现类似std :: vector.back()的东西 - Implementing something like std::vector.back() 我可以使用std :: vector :: operator []返回的引用多长时间? - how long can I use reference returned by std::vector::operator[]? 使用c ++,我如何实现自己的容器类的itetator,以便我可以使用类似std :: sort() - using c++, how do I implement itetator to my own container class so i can use something like std::sort() 如何初始化std :: map项的std :: vector? - How can I initialize an std::vector of std::map items?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM