2016-01-18 21 views
12

Giả sử chúng ta có một class với một std::mutex:Thực hiện hoán đổi cho lớp học với std :: mutex

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

cách thích hợp để thực hiện các phương pháp swap ở đây là gì? Có cần thiết/an toàn để khóa mỗi mutex riêng biệt và sau đó trao đổi tất cả mọi thứ? ví dụ.

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 
} 

I have seen that trong C++ 17, std::lock_guard sẽ có một constructor uống nhiều loại mutexes để tránh bế tắc, nhưng tôi không chắc chắn nếu đó là một vấn đề ở đây?

Trả lời

11

Bạn có thể sử dụng std::lock() để lấy khóa theo cách không bẻ khóa.

Nếu bạn muốn sử dụng std::lock_guard, có họ thông qua ổ khóa một lần thực hiện:

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

Nếu bạn thích std::unique_lock, sau đó xây dựng cho họ mà không cần khóa, sau đó gọi std::lock() để nhốt chúng cả (điều này cũng làm việc với 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_); 

Trong cả hai trường hợp, bạn nên thử nghiệm đầu tiên cho lhsrhs là cùng một đối tượng, bởi vì sử dụng std::lock với một mutex hai lần là hành vi không xác định:

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

Tôi không nghĩ rằng triển khai trao đổi của bạn là an toàn. Nếu một thuật toán khác cố gắng khóa rhs.mutex_ trước và sau đó lhs.mutex_, bạn có thể kết thúc bằng bế tắc. Thay vào đó, hãy thử dùng std::lock().

Các vấn đề liên quan