c++的单线程 mutex 问题

2023-08-14 19:48:40 +08:00
 Attenton

今天在学习std::recursive_mutex的时候看到std::recursive_mutex的应用场景是可重入申请锁,于是写了代码进行测试,确实在循环调用的时候不会出现死锁了。之后又用mutex进行了测试,但是发现在不同场景下会有不同的情况,代码如下:

#include <iostream>
#include <mutex>
std::recursive_mutex recursive_mutex_;
std::mutex mutex_;

void func(int n) {
    std::lock_guard<std::mutex> lock(mutex_);
    std::cout << "n = " << n << std::endl;
    if (n > 0) {
        func(n - 1);
    }
}

int main() {
    func(5);
    return 0;
}

按照预期,以上代码应该在打印了 5 之后就一直卡主,可是在 gcc 7.5 下,直接打印了 5 4 3 2 1 ;测试了很多在线编译器都是这个现象,只有https://www.onlinegdb.com/online_c++_compiler 这个在线编译器的运行结果符合预期。

请问各位大佬,这个现象的原因是什么? 应该不是 lock_guard 的问题,因为我自己手写了 lock_guard 查看了 lock 的声明周期,确实是在 func 执行结束之后才 unlock 。

1339 次点击
所在节点    C++
12 条回复
Attenton
2023-08-14 19:51:01 +08:00
补充:CmakeLists.txt 里面没有添加任何编译选项,cmake 版本是 3.18 , 用的 c++ 17
iwdmb
2023-08-14 19:52:07 +08:00
~/Project/C/testtest$ g++ --version
g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

~/Project/C/testtest$ ./a.out
n = 5
Attenton
2023-08-14 19:58:32 +08:00
@iwdmb
(base) root@ubuntu:/home# ./a.out
n = 5
n = 4
n = 3
n = 2
n = 1
n = 0
(base) root@ubuntu:/home# g++ --version
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

以上是我的执行结果,没搞懂是哪的问题
Attenton
2023-08-14 20:07:09 +08:00
If lock is called by a thread that already owns the mutex, the behavior is undefined: for example, the program may deadlock. An implementation that can detect the invalid usage is encouraged to throw a std::system_error with error condition resource_deadlock_would_occur instead of deadlocking.

If the mutex is currently locked by the same thread calling this function, it produces a deadlock (with undefined behavior).

是个 undefined behavior ,不能这样使用,结贴😓


Reference:
https://en.cppreference.com/w/cpp/thread/mutex/lock
https://cplusplus.com/reference/mutex/mutex/lock/
ysc3839
2023-08-14 20:07:33 +08:00
https://en.cppreference.com/w/cpp/thread/mutex/lock
If lock is called by a thread that already owns the mutex, the behavior is undefined
所以按照预期,以上代码发生什么事都有可能
Attenton
2023-08-14 20:09:54 +08:00
@ysc3839 嗯嗯,我也找到了,感谢
geelaw
2023-08-14 20:11:22 +08:00
n4849 § 32.5.3.2 没有定义 std::mutex 重入的情况,并且 § 32.5.3.2.1 提示

A program can deadlock if the thread that owns a mutex object calls lock() on that object. If the implementation can detect the deadlock, a resource_deadlock_would_occur error condition might be observed.

没有说必须死锁或者抛出异常。令 std::mutex 和 std::recursive_mutex 是一样的效果,是符合标准的。
Attenton
2023-08-14 20:15:37 +08:00
@geelaw 感谢,这个标准的定义可以在哪看?
geelaw
2023-08-14 20:24:55 +08:00
geelaw
2023-08-14 20:25:37 +08:00
Attenton
2023-08-14 20:41:04 +08:00
@geelaw 感谢
maplememory
2023-08-15 16:26:21 +08:00
虽说是 UB ,但通常应该是死锁才对。我估计是单线程没有竞态的原因。这样写就卡在 5 了
```c++
#include <iostream>
#include <mutex>
#include <thread>
std::mutex mtx;
int n = 5;

void func() {
std::lock_guard<decltype(mtx)> lock(mtx);
std::cout << "n = " << n-- << std::end;
if (n > 0) {
func();
}
}

int main() {
std::thread t([]{func();});
func();
t.join();
return 0;
}
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/965242

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX