实现 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_);
在这两种情况下,您应该首先测试 lhs
和 rhs
是否为同一个对象,因为将 std::lock
与一个互斥体一起使用两次是未定义的行为:
if (&lhs == &rhs)
return;
假设我们有一个 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_);
在这两种情况下,您应该首先测试 lhs
和 rhs
是否为同一个对象,因为将 std::lock
与一个互斥体一起使用两次是未定义的行为:
if (&lhs == &rhs)
return;