为什么要把 std::lock 放在 std::lock_guard 之前
Why put std::lock before std::lock_guard
继续 Concurrency In Action 我已经达到了以下示例。
作者说如果我们每次锁定 2 mutexes in the same order, then we are guaranteed to avoid deadlocks.
考虑书中的这个例子:
class X
{
private:
some_big_object some_detail;
std::mutex m;
public:
X(some_big_object const& sd):some_detail(sd){}
friend void swap(X& lhs, X& rhs)
{
if(&lhs==&rhs){return;}
std::lock(lhs.m,rhs.m);
std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock);
std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock);
swap(lhs.some_detail,rhs.some_detail);
}
};
- 为什么我们要应用
std::lock
and then apply 2 std::lock_guards
with std::adopt_lock
而不是一个接一个地应用 2 std::lock_guards
?
- 为什么我们不能只放这 2
std::mutex
es in the std::scoped_lock
?
std::lock
不是 RAII。不在 RAII 中的互斥锁是危险和可怕的。如果抛出异常,您可以 "leak" 锁定。
std::lock_guard
不支持死锁安全多重互斥锁。但它是 RAII,因此它使其余代码更安全。如果先在一个位置锁定 a,然后锁定 b,然后在另一个位置锁定 b,然后锁定 a,就会得到可能死锁的代码(一个线程持有 a 并等待 b,而另一个线程持有 b 并等待 a)。
std::lock
保证通过某种未指定的方式(可能包括锁的全局顺序)避免这种情况。
std::scoped_lock
是 c++17. In c++17 您应该使用它来代替您显示的示例代码。添加它是因为编写该代码很糟糕。名称重整和链接问题阻止了简单地向现有锁定原语(如锁守卫)添加可变参数支持,这就是它具有不同名称的原因。
原因是 std::lock
以某种未指定的顺序锁定互斥量,但所有线程中的顺序都是相同的,从而保护我们免受死锁。因此,它可能是 lock(lhs.m)
,然后是 lock(rhs.m)
,或者反过来。这意味着我们不知道先创建 std::lock_guard
中的哪一个:lhs.m
还是 rhs.m
。
这本书好像是以C++11为基础标准写的。 std::scoped_lock
仅出现在 C++17 中。
Why do we apply the std::lock and then apply 2 std::lock_guards with std::adopt_lock instead of just applying 2 std::lock_guards one after another??
如果您使用两个 std::lock_guard
without std::lock
,swap(a, b);
的锁定顺序将与 swap(b, a);
相反,其中 a
和 b
是 X
秒。如果一个线程尝试 swap(a, b);
而另一个线程尝试 swap(b, a);
它们可能会死锁。第一个线程将拥有 a
的互斥锁并等待 b
,而第二个线程将拥有 b
的互斥锁并等待 a
的。使用 std::lock
确保锁定顺序始终一致。
Why cant we just put this 2 std::mutexes in the std::scoped_lock??
如果您查看所链接文章的发布日期,c++17 还不存在。由于std::scoped_lock
是c++17引入的,所以不能在文章中使用。这种锁定问题是 std::scoped_lock
旨在解决的问题,应该在现代代码中使用。
继续 Concurrency In Action 我已经达到了以下示例。
作者说如果我们每次锁定 2 mutexes in the same order, then we are guaranteed to avoid deadlocks.
考虑书中的这个例子:
class X
{
private:
some_big_object some_detail;
std::mutex m;
public:
X(some_big_object const& sd):some_detail(sd){}
friend void swap(X& lhs, X& rhs)
{
if(&lhs==&rhs){return;}
std::lock(lhs.m,rhs.m);
std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock);
std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock);
swap(lhs.some_detail,rhs.some_detail);
}
};
- 为什么我们要应用
std::lock
and then apply 2std::lock_guards
withstd::adopt_lock
而不是一个接一个地应用 2std::lock_guards
? - 为什么我们不能只放这 2
std::mutex
es in thestd::scoped_lock
?
std::lock
不是 RAII。不在 RAII 中的互斥锁是危险和可怕的。如果抛出异常,您可以 "leak" 锁定。
std::lock_guard
不支持死锁安全多重互斥锁。但它是 RAII,因此它使其余代码更安全。如果先在一个位置锁定 a,然后锁定 b,然后在另一个位置锁定 b,然后锁定 a,就会得到可能死锁的代码(一个线程持有 a 并等待 b,而另一个线程持有 b 并等待 a)。
std::lock
保证通过某种未指定的方式(可能包括锁的全局顺序)避免这种情况。
std::scoped_lock
是 c++17. In c++17 您应该使用它来代替您显示的示例代码。添加它是因为编写该代码很糟糕。名称重整和链接问题阻止了简单地向现有锁定原语(如锁守卫)添加可变参数支持,这就是它具有不同名称的原因。
原因是
std::lock
以某种未指定的顺序锁定互斥量,但所有线程中的顺序都是相同的,从而保护我们免受死锁。因此,它可能是lock(lhs.m)
,然后是lock(rhs.m)
,或者反过来。这意味着我们不知道先创建std::lock_guard
中的哪一个:lhs.m
还是rhs.m
。这本书好像是以C++11为基础标准写的。
std::scoped_lock
仅出现在 C++17 中。
Why do we apply the std::lock and then apply 2 std::lock_guards with std::adopt_lock instead of just applying 2 std::lock_guards one after another??
如果您使用两个 std::lock_guard
without std::lock
,swap(a, b);
的锁定顺序将与 swap(b, a);
相反,其中 a
和 b
是 X
秒。如果一个线程尝试 swap(a, b);
而另一个线程尝试 swap(b, a);
它们可能会死锁。第一个线程将拥有 a
的互斥锁并等待 b
,而第二个线程将拥有 b
的互斥锁并等待 a
的。使用 std::lock
确保锁定顺序始终一致。
Why cant we just put this 2 std::mutexes in the std::scoped_lock??
如果您查看所链接文章的发布日期,c++17 还不存在。由于std::scoped_lock
是c++17引入的,所以不能在文章中使用。这种锁定问题是 std::scoped_lock
旨在解决的问题,应该在现代代码中使用。