V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Attenton
V2EX  ›  C++

c++的单线程 mutex 问题

  •  
  •   Attenton · 2023-08-14 19:48:40 +08:00 · 1280 次点击
    这是一个创建于 452 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天在学习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 。

    12 条回复    2023-08-15 16:26:21 +08:00
    Attenton
        1
    Attenton  
    OP
       2023-08-14 19:51:01 +08:00
    补充:CmakeLists.txt 里面没有添加任何编译选项,cmake 版本是 3.18 , 用的 c++ 17
    iwdmb
        2
    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
        3
    Attenton  
    OP
       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
        4
    Attenton  
    OP
       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
        5
    ysc3839  
       2023-08-14 20:07:33 +08:00 via Android   ❤️ 1
    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
        6
    Attenton  
    OP
       2023-08-14 20:09:54 +08:00
    @ysc3839 嗯嗯,我也找到了,感谢
    geelaw
        7
    geelaw  
       2023-08-14 20:11:22 +08:00   ❤️ 1
    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
        8
    Attenton  
    OP
       2023-08-14 20:15:37 +08:00
    @geelaw 感谢,这个标准的定义可以在哪看?
    geelaw
        10
    geelaw  
       2023-08-14 20:25:37 +08:00
    Attenton
        11
    Attenton  
    OP
       2023-08-14 20:41:04 +08:00
    @geelaw 感谢
    maplememory
        12
    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;
    }
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2786 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 14:05 · PVG 22:05 · LAX 06:05 · JFK 09:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.