实现 class 与 std::mutex 的交换

Implementing swap for class with std::mutex

假设我们有一个 class 和一个 std::mutex:

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

在这里实施 swap 方法的合适方法是什么?是不是 required/safe 分别锁定每个互斥锁然后交换所有东西?例如

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 采用多个互斥锁来避免死锁,但我不确定这是否是一个问题?

我认为您的交换实施不安全。如果另一个算法尝试先锁定 rhs.mutex_ 然后 lhs.mutex_,您可能会陷入死锁。请尝试 std::lock()

您可以使用 std::lock() 以 non-deadlocking 的方式获取锁。

如果您想使用 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_);

在这两种情况下,您应该首先测试 lhsrhs 是否为同一个对象,因为将 std::lock 与一个互斥体一起使用两次是未定义的行为:

if (&lhs == &rhs)
    return;