2016-01-18 40 views
12

Załóżmy, że mamy class z std::mutex:swapowych dla klasy wykonawcze w std :: mutex

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

Co jest właściwym sposobem wdrożenia metody swap tutaj? Czy jest wymagane/bezpieczne zablokowanie każdego muteksu osobno, a następnie zamiana wszystkiego? na przykład

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 
} 

Widziałem, że w C++ 17, std::lock_guard będzie miał constructor robienia wielu muteksy unikania impasu, ale nie jestem pewien, czy to jest problem tutaj?

Odpowiedz

11

Możesz użyć std::lock(), aby uzyskać zamki w sposób niezablokowany.

Jeśli chcesz użyć std::lock_guard, je przyjąć zamki raz zrobiono:

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_); 

Jeśli wolisz std::unique_lock, to konstruować je bez blokowania, a następnie zadzwonić std::lock() je zarówno zablokować (ta działa również z 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_); 

W obu przypadkach należy najpierw test lhs i rhs będąc tym samym przedmiotem, ponieważ używając std::lock z jednym muteksem dwukrotnie to niezdefiniowane zachowanie:

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

Nie sądzę, że implementacja zamiany jest bezpieczna. Jeśli inny algorytm spróbuje najpierw zablokować rhs.mutex_, a następnie lhs.mutex_, może się skończyć zakleszczeniem. Zamiast tego spróbuj std::lock().