Безопасный и эффективный способ установки мьютекса на входе в контейнер

С++ std::mutex не имеет конструктора перемещения. Существует хорошая причина для этого. В принципе, сами конструкторы перемещения обычно не являются потокобезопасными, и вся точка мьютекса состоит в том, что несколько потоков будут пытаться получить к нему доступ одновременно.

Несчастная импликация заключается в том, что мьютекс нельзя напрямую помещать в контейнер. Контейнеры должны иметь возможность безопасно перемещать свое содержимое, и вы не можете сделать это с помощью мьютекса.

Легкий выход - просто защитить весь контейнер с помощью одного отдельного мьютекса. Но предположим, что мне нужен более тонкий контроль? Если я реализую базу данных через контейнер (например: std::map), представляется разумной необходимость блокировки отдельных записей, а не только всей базы данных.

Следующее, что приходит в голову, - это взломать проблему, используя std::unique_ptr. Это будет скомпилировано, но на самом деле это не изменяет основную проблему, не так ли? Сценарий, в котором возникает проблема с перемещением, заключается в том, что thread1 создает изменение контейнера, которое вызывает перемещение записи, а thread2 находится в середине использования этой записи контейнера. В этом случае thread2 может так же легко привести к уничтожению записи или умному указателю. Кажется, что бы вы ни делали, вам нужно заблокировать весь контейнер с помощью мьютекса, прежде чем что-либо делать.

Кажется, что для подобных вещей должна быть известная идиома.

Ответ 1

Мьютекс не требует перемещения:

Представьте, что каждая строка на вашей карте похожа:

template <class T>
class row
{
    shared_ptr<mutex> m;
    T data;
    ...
};

Итак, если ваша строка должна быть перемещена или скопирована, нет проблем.

Затем вы можете получить доступ к мьютексу из каждого процесса для доступа к данным.

Конечно, для выполнения изменений на всей карте требуется глобальный мьютекс: insert/delete/[]/любая другая операция, которая изменяет состояние карты.

Редакция:

Следуя простому примеру кода с мьютексом в каждой строке. (Он не реализует ничего, что только структура данных)

#include <memory>
#include <map>
#include <mutex>

template <class T>
class row
{
    std::shared_ptr<std::mutex> m;
    T data;
public:
    row( std::shared_ptr<std::mutex> mut): m(mut){};
};

auto main () -> int
{
    std::shared_ptr<std::mutex> mut(new std::mutex);
    std::map<int,row<int>> db;
    row<int> a(mut);
    db.insert(std::pair<int, row<int>>(1, a));
    return 0;
}