2016-01-18 2 views
12

Предположим, что мы имеем class с std::mutex:Реализация своп для класса с станд :: мьютекс

class Foo 
{ 
    std::mutex mutex_; 
    std::string str_; 
    // other members etc 
public: 
    friend void swap(Foo& lhs, Foo& rhs) noexcept; 
} 

Что является подходящим способом для реализации метода swap здесь? Требуется ли/безопасно блокировать каждый мьютекс отдельно, а затем менять все? например

void swap(Foo& lhs, Foo& rhs) noexcept 
{ 
    using std::swap; 
    std::lock_guard<std::mutex> lock_lhs {lhs.mutex_}, lock_rhs {rhs.mutex_}; 
    swap(ls.str_, rhs.str_); 
    // swap everything else 
} 

Я видел, что в C++ 17, std::lock_guard будет иметь constructor принимая несколько семафоров для предотвращения тупиковой ситуации, но я не уверен, что это проблема здесь?

ответ

11

Вы можете использовать std::lock() для приобретения замков неблокирующим способом.

Если вы хотите использовать std::lock_guard, у них принимают замки занятых:

std::lock(lhs.mutex_, rhs.mutex_); 
std::lock_guard<std::mutex> lock_a(lhs.mutex_, std::adopt_lock); 
std::lock_guard<std::mutex> lock_b(rhs.mutex_, std::adopt_lock); 
//swap actions 
swap(ls.str_, rhs.str_); 

Если вы предпочитаете std::unique_lock, затем построить их без блокировки, а затем вызвать std::lock() их как заблокировать (это также работает с std::lock_guard):

std::unique_lock<std::mutex> lock_a(lhs.mutex_, std::defer_lock); 
std::unique_lock<std::mutex> lock_b(rhs.mutex_, std::defer_lock); 
std::lock(lock_a, lock_b); 
//swap actions 
swap(ls.str_, rhs.str_); 

В обоих случаях, вы должны первым испытанием для lhs и rhs быть тем же объектом, потому что с помощью std::lock с одним мьютеком дважды неопределенное поведение:

if (&lhs == &rhs) 
    return; 
2

Я не думаю, что ваша своп-реализация безопасна. Если другой алгоритм пытается сначала заблокировать rhs.mutex_, а затем lhs.mutex_, вы можете оказаться в тупике. Попробуйте вместо этого std::lock().

Смежные вопросы