3

语境:

我知道std::lock_guard自从std::scoped_lock.

我也知道这std::scoped_lock是首选,因为它可以处理多个互斥体并使用与死锁避免算法相同的方式std::lock

我对我们只有一个互斥锁的情况感兴趣,因此我们不需要关心避免死锁。

我从这个答案中读到:

你可以考虑std::lock_guard弃用。的单参数案例std::scoped_lock可以作为一种特化来实现,这样您就不必担心可能出现的性能问题。

问题:

我想知道这句话有多少是真的。

我的意思是,是否保证(按标准)使用单个互斥锁std::scoped_lock将被专门化,以便消除由于避免死锁处理而导致的任何不必要的开销?


我的想法:

在对该问题进行了一些调查后,我从cppreference中发现了以下句子:

如果给定了多个互斥锁,则使用死锁避免算法,就像 by 一样std::lock

这可以让我们推断出这样的事情不会发生(即如果只给出一个互斥锁)。
但再一次,这只是一个假设。

从这个c++ 草案中,我没有看到任何关于这种专业化的明确提及。
我得到的唯一一句话是:

sizeof...(MutexTypes)1时,提供的Mutex类型应满足Cpp17BasicLockable要求。否则,每个互斥锁类型都应满足Cpp17Lockable要求。

(强调我的)

我知道BasicLockable要求要求存在满足此处定义的条件lock()的功能。 另一方面,Lockable要求假定BasicLockable要求,并添加了满足其中定义的条件的功能。unlock()
try_lock()

我知道该try_lock()函数是运行std::lock.

从上面的草稿摘录中所述,try_lock()如果我们只给std::scoped_lock.
这是否足以推断/考虑始终定义上述专业化(并且可能表现得std::lock_guard如此)。
我会说是的,但由于我从未看到任何明确提及它,我想知道我是对的还是我错过了什么?


编辑:

我刚刚注意到我错过了这里最重要的部分:

效果pm:用初始化tie(m...)。那么如果sizeof...(MutexTypes)0,则没有影响。否则,如果sizeof...(MutexTypes)1,那么m.lock()否则,lock(m...)

(强调我的)

std::lock只有当有多个给定的互斥体时才会调用它回答我的询问。我应该在问问题之前看到它...

4

3 回答 3

3

std::scoped_lock需要与std::lock_guard仅提供一个互斥锁时的行为相同。因此,对单个互斥体情况的不同要求。

这可以通过专门化来完成,或者可以通过不同的内部机制来完成,只要行为相同。

于 2020-01-29T12:24:12.800 回答
2

如果您阅读lock_guard(就在上面scoped_lock)的规范,它应该很清楚。

[thread.lock.guard]-3

用 m 初始化 pm。调用 m.lock()

[thread.lock.scoped]-3

用 tie(m...) 初始化 pm。[...] 否则,如果 sizeof...(MutexTypes) 为 1,则为 m.lock()。[...]

它没有明确提到要使用lock_guard,但它必须具有相同的行为。

于 2020-01-29T12:31:39.193 回答
0

该标准很少会通过专业化来保证某种优化(值得注意的例子是不同迭代器类型的专业化 a 和令人憎恶的是std::vector<bool>)。为此,有两种方法:

  1. 相信你的编译器/标准库实现。编译器是史诗级的,它们进行了极其高级的优化,其中一些是你梦寐以求的。在大多数情况下,STL 的实现都很棒。有些地方它们速度较慢,因为它们必须能够处理奇怪的边缘情况,但是这里必须已经有了不同的专业化,因为一个参数情况只需要 BasicLockable ,所以它将有一个不需要的实现try_lock,那么为什么它不应该是有效的。
  2. 执行您的代码。测试它是否足够快,测试它是否scoped_lock在你的代码的热门路径上,如果你真的认为(并且有数据证明它)scoped_lock很慢,那么只有然后替换它lock_guard并再次测试。
于 2020-01-29T12:32:18.463 回答