2016-01-18 70 views
12

Supponiamo di avere un class con un std::mutex:Implementazione di swap per classe con std :: mutex

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

Qual è il modo adeguato per implementare il metodo swap qui? È necessario/sicuro bloccare ogni mutex separatamente e quindi scambiare tutto? per esempio.

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 
} 

ho visto che in C++ 17, std::lock_guard avrà un constructor che assumevano più mutex per evitare situazione di stallo, ma non sono sicuro se questo è un problema qui?

risposta

11

È possibile utilizzare std::lock() per acquisire i blocchi in modo non bloccabile.

Se si desidera utilizzare std::lock_guard, li hanno adottano le serrature una volta prese:

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

Se preferite std::unique_lock, poi li costruiscono senza bloccare, quindi chiamare std::lock() per bloccare entrambi (questo funziona anche con 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_); 

In entrambi i casi, si dovrebbe primo test per lhs e rhs essere lo stesso oggetto, perché l'utilizzo di std::lock con un mutex due volte non è definito comportamento:

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

Non penso che l'implementazione dello swap sia sicura. Se un altro algoritmo tenta di bloccare prima rhs.mutex_ e poi lhs.mutex_, si può finire con un deadlock. Prova invece a std::lock().

Problemi correlati