c++的单线程 mutex 问题

270 天前
 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 。

1092 次点击
所在节点    C++
12 条回复
Attenton
270 天前
补充:CmakeLists.txt 里面没有添加任何编译选项,cmake 版本是 3.18 , 用的 c++ 17
iwdmb
270 天前
~/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
270 天前
@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
270 天前
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
270 天前
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
270 天前
@ysc3839 嗯嗯,我也找到了,感谢
geelaw
270 天前
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
270 天前
@geelaw 感谢,这个标准的定义可以在哪看?
geelaw
270 天前
geelaw
270 天前
Attenton
270 天前
@geelaw 感谢
maplememory
269 天前
虽说是 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