Старый вопрос, новый ответ:
Imho, лучший способ справиться с проблемой тупиковой блокировки оператора присваивания оригинальной копии:
A& operator=(const A& other)
{
if (this!=&other) {
std::unique_lock<std::mutex> _mylock(mtx, std::defer_lock),
_otherlock(other.mtx, std::defer_lock);
std::lock(_mylock, _otherlock);
i = other.i;
}
return *this;
}
т.е. используйте std::lock(L1, L2)
для одновременной блокировки двух взаимных блокировок, не опасаясь тупиковой ситуации. Вероятно, это будет более высокая производительность, чем идиома копирования / обмена, особенно если данные члена состоят из std::vector
, std::string
или типов, которые содержат векторы и / или строки.
В C ++ 1y (мы надеемся, что y равно 4), есть новый заголовок <shared_mutex>
, обеспечивающий возможность блокировки чтения / записи, который может обеспечить повышение производительности (тестирование производительности будет необходимо для конкретных случаи использования, чтобы подтвердить это). Вот как это будет использоваться:
#include <mutex>
#include <shared_mutex>
class A {
private:
int i;
mutable std::shared_mutex mtx;
public:
A() : i(), mtx() { }
A(const A& other) : i(), mtx()
{
std::shared_lock<std::shared_mutex> _lock(other.mtx);
i = other.i;
}
A& operator=(const A& other)
{
if (this!=&other) {
std::unique_lock<std::shared_mutex> _mylock(mtx, std::defer_lock);
std::shared_lock<std::shared_mutex> _otherlock(other.mtx, std::defer_lock);
std::lock(_mylock, _otherlock);
i = other.i;
}
return *this;
}
int get() const
{
std::shared_lock<std::shared_mutex> _mylock(mtx);
return i;
}
};
т.е. это очень похоже на исходный код (модифицированный для использования std::lock
, как я делал выше). Но тип мьютекса члена теперь std::shared_mutex
вместо std::mutex
. И при защите const A
(и при условии отсутствия изменяемых членов, кроме мьютекса), нужно только заблокировать мьютекс в «режиме совместного использования». Это легко сделать с помощью shared_lock<shared_mutex>
. Когда вам нужно заблокировать мьютекс в «эксклюзивном режиме», вы можете использовать unique_lock<shared_mutex>
или lock_guard<shared_mutex>
, в зависимости от ситуации, и таким же образом, как если бы вы использовали это средство с std::mutex
.
В частности, обратите внимание, что теперь многие потоки могут одновременно копировать конструкции из одного A
или даже копировать присваивать из того же A
. Но только один поток все еще может скопировать присвоить в одно и то же A
за один раз.