示例化为RValues的lock_guards能在C++中工作吗?

eqqqjvef  于 2023-06-25  发布在  其他
关注(0)|答案(1)|浏览(140)

在C++中,堆栈对象通常按出现的顺序创建,当它们超出作用域时按相反的顺序销毁。
但是,这只适用于L值,而不是R值,如下面代码on Godbolt所示:

  1. #include <iostream>
  2. class C{
  3. int instanceNo;
  4. public:
  5. C(){
  6. static int instanceCount = 0;
  7. instanceNo = ++instanceCount;
  8. std::cout << "constructed " << instanceNo << "\n";
  9. }
  10. ~C(){
  11. std::cout << "destructed " << instanceNo << "\n";
  12. }
  13. };
  14. int main()
  15. {
  16. C one;
  17. C two;
  18. C{}; // three as RValue
  19. C four;
  20. C five;
  21. }

结果:RValue立即被销毁,而不是在main()结束时。到目前为止,这对我来说很好。
我们有一些C++14代码如下:

  1. std::lock_guard<std::mutex> guard(mutex);

在C++17中,我们可以使用模板参数推导(CTAD)并将代码缩短为

  1. std::lock_guard guard(mutex);

然而,一位同事把它写得更短了:

  1. std::lock_guard{mutex};

在我的理解中,这现在是一个R值。它将在构造时锁定互斥体,这很好。然而,根据上面的演示,它的生命周期立即结束,从而解锁互斥锁并有效地删除保护。
这个结论是正确的吗?这个改变破坏了我们类的线程安全性吗?

mo49yndu

mo49yndu1#

简而言之,你的分析是100%正确的。
如果你想专门测试std::lock_guard:它只需要一个实现.lock().unlock()的类,所以你可以创建一个只打印调试输出的伪锁:

  1. #include <iostream>
  2. #include <mutex>
  3. struct dummy_mutex
  4. {
  5. void lock()
  6. {
  7. std::cout << "Locked!\n" << std::flush;
  8. }
  9. void unlock()
  10. {
  11. std::cout << "Unlocked!\n" << std::flush;
  12. }
  13. };
  14. int main()
  15. {
  16. dummy_mutex mtx;
  17. std::cout << "A\n" << std::flush;
  18. {
  19. std::cout << "B\n" << std::flush;
  20. std::lock_guard{mtx};
  21. std::cout << "C\n" << std::flush;
  22. }
  23. std::cout << "D\n" << std::flush;
  24. return 0;
  25. }

这将打印:

  1. A
  2. B
  3. Locked!
  4. Unlocked!
  5. C
  6. D

如果将该行更改为std::lock_guard guard{mtx};,则输出将更改为:

  1. A
  2. B
  3. Locked!
  4. C
  5. Unlocked!
  6. D
展开查看全部

相关问题