Простой способ сделать класс потокобезопасным — добавить атрибут мьютекса и заблокировать мьютекс в методах доступа.
class cMyClass {
boost::mutex myMutex;
cSomeClass A;
public:
cSomeClass getA() {
boost::mutex::scoped_lock lock( myMutex );
return A;
}
};
Проблема в том, что это делает класс некопируемым.
Я могу заставить все работать, сделав мьютекс статическим. Однако это означает, что каждый экземпляр класса блокируется при доступе к любому другому экземпляру, поскольку все они используют один и тот же мьютекс.
Интересно, есть ли лучший способ?
Я пришел к выводу, что лучшего пути нет. Лучше всего сделать класс потокобезопасным с атрибутом private static mutex: - это просто, это работает и скрывает неудобные детали.
class cMyClass {
static boost::mutex myMutex;
cSomeClass A;
public:
cSomeClass getA() {
boost::mutex::scoped_lock lock( myMutex );
return A;
}
};
Недостатком является то, что все экземпляры класса совместно используют один и тот же мьютекс и поэтому блокируют друг друга без необходимости. Это нельзя вылечить, сделав атрибут мьютекса нестатическим (чтобы дать каждому экземпляру собственный мьютекс), потому что сложности копирования и присвоения кошмарны, если все сделано правильно.
Отдельные мьютексы, при необходимости, должны управляться внешним некопируемым синглтоном со ссылками, установленными для каждого экземпляра при создании.
Спасибо за все ответы.
Несколько человек упомянули о написании моего собственного конструктора копирования и оператора присваивания. Я попробовал это. Проблема в том, что мой реальный класс имеет много атрибутов, которые постоянно меняются во время разработки. Сопровождение как конструктора копирования, так и оператора присваивания утомительно и подвержено ошибкам, а ошибки создают трудно обнаруживаемые ошибки. Позволить компилятору сгенерировать их для сложного класса — это огромная экономия времени и уменьшение числа ошибок.
Многие ответы касаются того, чтобы сделать конструктор копирования и оператор присваивания потокобезопасными. Это требование еще больше усложняет все это! К счастью для меня, мне это не нужно, так как все копирование выполняется во время настройки в одном потоке.
Теперь я думаю, что лучшим подходом было бы создание крошечного класса, содержащего только мьютекс и критические атрибуты. Затем я могу написать небольшой конструктор копии и оператор присваивания для критического класса и оставить компилятору заботиться обо всех остальных атрибутах в основном классе.
class cSafe {
boost::mutex myMutex;
cSomeClass A;
public:
cSomeClass getA() {
boost::mutex::scoped_lock lock( myMutex );
return A;
}
(copy constructor)
(assignment op )
};
class cMyClass {
cSafe S;
( ... other attributes ... )
public:
cSomeClass getA() {
return S.getA();
}
};