【问题标题】:std::scoped_lock behaviour with a single mutex具有单个互斥锁的 std::scoped_lock 行为
【发布时间】:2020-01-29 12:11:10
【问题描述】:

上下文:

我知道自从std::scoped_lock 一起出现以来,std::lock_guard 已经被弃用了。

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

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

我从this answer 读到:

您可以认为std::lock_guard 已弃用。 std::scoped_lock 的单参数案例可以实现为特化,这样您就不必担心可能出现的性能问题。

问题:

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

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


我的想法:

对问题进行了一番调查,我从cppreference找到了以下句子:

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

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

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

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

(强调我的)

我知道 BasicLockable 要求要求存在满足 here 等条件的 lock()unlock() 函数。
另一方面,Lockable 要求假定 BasicLockable 要求,并添加了满足 there 等条件的 try_lock() 函数。

我知道try_lock() 函数是运行std::lock 使用的死锁避免算法所必需的。

根据上述 草稿摘录中所述,如果我们只给std::scoped_lock 提供一个互斥锁,则不需要try_lock() 函数。
这是否足以推断/考虑始终定义上述专业化(并且可能表现得像 std::lock_guard 那样)。
我会说是的,但由于我从未看到任何明确提及它,我想知道我是对的还是我错过了什么?


编辑:

我刚刚注意到我错过了最重要的部分here,它指出:

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

(强调我的)

这回答了我的询问,std::lock 仅在存在多个给定互斥体时才被调用。我应该在问问题之前看到它......

【问题讨论】:

    标签: c++17 c++ c++ language-lawyer c++17 mutex scoped-lock


    【解决方案1】:

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

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

    【讨论】:

    • 感谢您的确认。其实我最想念的是important part。在问我的问题之前我应该​​注意到它:)
    【解决方案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,但它必须具有相同的行为。

    【讨论】:

    • 是的,我注意到这部分太晚了(在我的编辑中提到)。
    【解决方案3】:

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

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

    【讨论】:

    • 同意,但我不能依赖可能的编译器优化来发布与编译器无关的一般确定性。我可以信任的唯一保证是标准给出的保证。潜在的问题是,我想知道我是否可以完全摆脱 std::lock_guard,并保证仅使用一个互斥体就不会产生任何不必要的开销。
    猜你喜欢
    • 2010-11-18
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-04
    • 2012-06-05
    • 2018-05-23
    相关资源
    最近更新 更多