Безопасный и эффективный способ установки мьютекса на входе в контейнер
С++ 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;
}