Заимствуем пример Говарда Хиннанта и модифицируем его для использования копирования и замены , это op= потокобезопасный?
struct A {
A() = default;
A(A const &x); // Assume implements correct locking and copying.
A& operator=(A x) {
std::lock_guard<std::mutex> lock_data (_mut);
using std::swap;
swap(_data, x._data);
return *this;
}
private:
mutable std::mutex _mut;
std::vector<double> _data;
};
Я считаю, что это потокобезопасно (помните, что параметр op= передается по значению), и единственная проблема, которую я могу найти, это та, что скрыта под ковром: ctor копирования. Однако это будет редкий класс, который допускает копирование-присваивание, но не копирование-конструкцию, так что проблема одинаково существует в обоих вариантах.
Учитывая, что самоназначение настолько редко (по крайней мере, для этого примера), что я не возражаю против дополнительной копии, если это произойдет, считайте потенциальную оптимизацию этого != &rhs либо незначительной, либо пессимизацией. Есть ли какая-либо другая причина предпочесть или избегать ее по сравнению с исходной стратегией (ниже)?
A& operator=(A const &rhs) {
if (this != &rhs) {
std::unique_lock<std::mutex> lhs_lock( _mut, std::defer_lock);
std::unique_lock<std::mutex> rhs_lock(rhs._mut, std::defer_lock);
std::lock(lhs_lock, rhs_lock);
_data = rhs._data;
}
return *this;
}
Между прочим, я думаю, что это кратко обрабатывает ctor копирования, по крайней мере, для этого класса, даже если это немного бестолково:
A(A const &x) : _data {(std::lock_guard<std::mutex>(x._mut), x._data)} {}