Есть ли минусы, которые могут заставить не использовать подобные обертки.
Также подскажите, есть ли уже что-то подобное в stl или boost.
Вот накидал пример:
SafeRef:
#include
template
SafeRef(std::mutex &mutex, RefType ref) : m_reference(ref), m_mutex(mutex)
{
m_mutex.lock();
}
SafeRef(SafeRef &&other)
: m_mutex(other.m_mutex), m_reference(other.m_referance)
{
}
RefType get() const { return m_reference; }
~SafeRef() { m_mutex.unlock(); }
private:
std::mutex &m_mutex;
RefType m_reference;
};
Тестовый код:
class SomeObj
{
public:
SafeRef
void write(SomeObj &obj)
{
obj.reserve(500);
for (int i = 0; i < 500; i++)
{
obj.addElement(i);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void read(const SomeObj &obj)
{
for (int i = 0; i < 5; i++)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
auto v = obj.getVector();
std::cout << v.get().size() << std::endl;
}
}
int main()
{
SomeObj obj;
std::thread thread1(&write, std::ref(obj));
std::thread thread2(&read, std::ref(obj));
thread2.join();
thread1.join();
return 0;
}
Ответ
Проблема этого подхода в том, что потокобезопасность намного сложнее
Вы не можете просто так сделать объект потокобезопасным, защитив все операции мьютексом. К примеру, у вас есть обёртка над стеком, дававйте посмотрим тогда на код
SupposedlySafeStack
Проблема здесь в том, что между выполнением if (!s.is_empty()) и s.pop(); стек вполне мог стать пустым.
Для хорошего потокобезопасного стека нужен другой набор операций. Например, атомарный bool try_pop(T&) и push_range(Iterator begin, Iterator end). Потокобезопасность нужно планировать на уровне внешнего интерфейса класса.
То же относится к доступу по индексу к std::vector и так далее.
Поэтому обёртки, которые оборачивают все публичные функции, непригодны: они лишь создают ложное чувство безопасности!
А обёртка на целый объект уже существует, это std::lock_guard
Кроме того, обычно вам нужно сделать потокобезопасным не один объект, а группу объектов и их отношения между собой. Например, у вас есть очередь и стек, как в алгоритме сортировочной станции; нет смысла иметь отдельно защищённый стек и отдельно очередь, потому что при этом они могут рассогласоваться во время вашей операции! Например, если вы переносите все элементы из стека в очередь, то после удаления всех элементов из стека перед добавлением в очередь в стеке могут образоваться новые элементы, и инвариант вашего алгоритма нарушится.
Вам придётся заводить явный мьютекс, и защищать не объекты, а ваши операции с объектами