Как создать интеллектуальный указатель, который блокирует и разблокирует мьютексы?
У меня есть поточный класс, из которого я хотел бы иногда получать указатель переменной экземпляра. Я хотел бы, чтобы этот доступ был защищен мьютексом, чтобы поток был заблокирован от доступа к этому ресурсу, пока клиент не завершит свой указатель.
Мой первоначальный подход к этому - вернуть пару объектов: один указатель на ресурс и один shared_ptr на объект блокировки на мьютексе. Этот shared_ptr содержит единственную ссылку на объект блокировки, поэтому мьютекс должен быть разблокирован, когда он выходит из области видимости. Что-то вроде этого:
void A::getResource()
{
Lock* lock = new Lock(&mMutex);
return pair<Resource*, shared_ptr<Lock> >(
&mResource,
shared_ptr<Lock>(lock));
}
Это решение является менее идеальным, поскольку требует, чтобы клиент держался за всю пару объектов. Такое поведение нарушает безопасность потока:
Resource* r = a.getResource().first;
Кроме того, моя собственная реализация этого является взаимоблокировкой, и у меня возникают трудности в определении того, почему, возможно, что-то не так.
То, что я хотел бы иметь, это shared_ptr, который содержит блокировку как переменную экземпляра, привязывая ее к средствам доступа к ресурсу. Это похоже на то, что должно иметь установленный образец дизайна, но, проведя некоторые исследования, я с удивлением обнаружил, что его трудно встретить.
Мои вопросы:
- Существует ли общая реализация этого шаблона?
- Есть ли проблемы с помещением мьютекса внутри shared_ptr, который я пропускаю, чтобы предотвратить распространение этого шаблона?
- Есть ли веская причина не реализовывать мой собственный класс shared_ptr для реализации этого шаблона?
(NB Я работаю над кодовой базой, которая использует Qt, но, к сожалению, не может использовать boost в этом случае. Однако ответы, связанные с повышением, по-прежнему представляют общий интерес.)
Ответы
Ответ 1
Я не уверен, есть ли какие-либо стандартные реализации, но поскольку мне нравится переустанавливать материал без причины, вот версия, которая должна работать (при условии, что вы не хотите копировать такие указатели):
template<class T>
class locking_ptr
{
public:
locking_ptr(T* ptr, mutex* lock)
: m_ptr(ptr)
, m_mutex(lock)
{
m_mutex->lock();
}
~locking_ptr()
{
if (m_mutex)
m_mutex->unlock();
}
locking_ptr(locking_ptr<T>&& ptr)
: m_ptr(ptr.m_ptr)
, m_mutex(ptr.m_mutex)
{
ptr.m_ptr = nullptr;
ptr.m_mutex = nullptr;
}
T* operator ->()
{
return m_ptr;
}
T const* operator ->() const
{
return m_ptr;
}
private:
// disallow copy/assignment
locking_ptr(locking_ptr<T> const& ptr)
{
}
locking_ptr& operator = (locking_ptr<T> const& ptr)
{
return *this;
}
T* m_ptr;
mutex* m_mutex; // whatever implementation you use
};
Ответ 2
Вы описываете вариант EXECUTE AROUND POINTER, описанный Кевлином Хенни в Выполнение последовательности последовательности.
У меня есть прототипная реализация на exec_around.h
, но я не могу гарантировать, что он работает правильно во всех случаях, поскольку это незавершенная работа, Он включает в себя функцию mutex_around
, которая создает объект и обертывает его интеллектуальным указателем, который блокирует и разблокирует мьютекс при доступе.