Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/139.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何调试罕见的死锁?_C++_Multithreading_C++11_Threadpool_Deadlock - Fatal编程技术网

C++ 如何调试罕见的死锁?

C++ 如何调试罕见的死锁?,c++,multithreading,c++11,threadpool,deadlock,C++,Multithreading,C++11,Threadpool,Deadlock,我正在尝试调试一个很少出现死锁的自定义线程池实现。所以我不能使用像gdb这样的调试器,因为在出现死锁之前,我已经单击了100次“启动”调试器 目前,我正在shell脚本的无限循环中运行threadpool测试,但这意味着我看不到变量等等。我正在尝试std::cout数据,但这会减慢线程速度并降低死锁风险,这意味着我可以在收到消息之前等待1个小时。然后我没有得到错误,我需要更多的消息,这意味着再等一个小时 如何有效地调试程序,使其反复重新启动,直到死锁?(或者我应该用所有的代码打开另一个问题寻求帮

我正在尝试调试一个很少出现死锁的自定义线程池实现。所以我不能使用像gdb这样的调试器,因为在出现死锁之前,我已经单击了100次“启动”调试器

目前,我正在shell脚本的无限循环中运行threadpool测试,但这意味着我看不到变量等等。我正在尝试
std::cout
数据,但这会减慢线程速度并降低死锁风险,这意味着我可以在收到消息之前等待1个小时。然后我没有得到错误,我需要更多的消息,这意味着再等一个小时

如何有效地调试程序,使其反复重新启动,直到死锁?(或者我应该用所有的代码打开另一个问题寻求帮助?)

提前谢谢你


附加问题:如何使用
std::condition\u变量检查一切是否正常?您无法真正判断哪个线程处于休眠状态,或者在
等待
条件下是否出现争用条件。

如果这是一种作业,则使用更多调试一次又一次地重新启动将是一种合理的方法

如果有人为你等待的每一个小时付钱,他们可能更愿意投资一个支持的软件,也就是说,一个记录程序所做的每一件事、每一条指令的软件,并允许你反复回放,反复调试。因此,您不需要添加更多的调试,而是记录发生死锁的会话,然后在死锁发生之前开始调试。你可以随心所欲地来回走动,直到你最终找到罪犯


链接中提到的软件实际上支持Linux和多线程。

您可以使用以下命令在循环中的GDB下运行测试用例:
GDB--eval command=run--eval command=quit--args./a.out

我自己也用过:
(而gdb--eval command=run--eval command=quit--args./thread\u testU;do echo..done)


一旦它死锁且不退出,您可以通过CTRL+C中断它进入调试器。

有两种基本方法:

  • 在调试器下自动运行程序。使用
    gdb程序-ex'run'-ex'quit'
    应该在调试器下运行程序,然后退出。如果程序仍然以一种或另一种形式存在(SEGFULT,或您手动破坏了它),将要求您进行确认
  • 在复制死锁后附加调试器。例如,gdb可以作为
    gdb
    运行,以附加到正在运行的程序-只需等待死锁并附加即可。当附加的调试器导致定时发生变化,并且您无法再重新编程错误时,这一点尤其有用

  • 这样你们就可以在循环中运行它,在你们喝咖啡的时候等待结果。顺便说一句-我发现第二个选项更容易。

    查找死锁的一个简单快速的调试方法是在需要调试的地方修改一些全局变量,然后在信号处理程序中打印它。您可以使用SIGINT(当您使用
    ctrl+c
    中断时发送)或SIGTERM(当您终止程序时发送):

    它将永远运行您的程序,直到它失败(
    $?
    是您程序的退出状态,您在信号处理程序中退出时出现退出失败)

    它对我来说很有效,特别是对于找出在什么锁之前和之后通过了多少线程


    它非常简单,但您不需要任何额外的工具,而且实现起来很快。

    Mozilla rr基于开源重播的调试

    但是有一个特定的开源实现值得一提:Mozilla
    rr

    首先执行记录运行,然后可以根据需要多次重播完全相同的运行,并在GDB中进行观察,它会保留所有内容,包括输入/输出和线程顺序

    报告提到:

    rr最初的动机是使间歇性故障的调试变得容易

    此外,
    rr
    允许GDB反向调试命令(如
    reverse next
    转到前一行),这使得查找问题的根本原因变得更加容易


    下面是一个运行中的
    rr
    的最小示例:

    调试死锁很困难。首先,确保所有的互斥锁和信号量都是按顺序解锁的
    std::cout
    可能是一个不好的工具,因为它会更改运行时行为和计时。可能会在这里有所帮助。否则,您可以正常运行程序,获得死锁,然后攻击gdb到死锁程序(请参阅)@πάνταῥεῖ 为什么解锁必须按相同的顺序进行?在大多数程序中,为了方便起见,它将以与锁定相反的顺序进行,但当您以任意顺序解锁时,我看不到任何错误的可能性(死锁通常是在您尝试锁定某些东西时,而不是解锁)。@πάνταῥεῖ 我仍然不知道为什么解锁顺序是个问题——只要在锁定过程中没有违反锁的层次结构,就我所知,它不应该有任何区别(除非我在脑海中交错时出错)。@MaciejPiechotka对不起,我删除了我愚蠢的评论。我的意思是,不同的线程在尝试锁定任何同步功能时应该使用完全相同的顺序(当然,要按照相应的顺序解锁它们)。完全同意附加。这是我经常做的&99%的时间它告诉我我想要什么。这是一个个人项目,所以它介于两者之间:)我不知道基于回放的调试,这是一个明智的建议。我想接受这个答案,但它并不能准确回答这个问题,我不能接受2,所以我给+1。非常感谢你!现在还有一个很有价值的开源实现:Mozilla rr:感谢您提供了明确的命令。我个人尝试了
    -batch
    ,但是h
    int dbg;
    
    int multithreaded_function()
    {
      signal(SIGINT, dbg_sighandler);
      ...
      dbg = someVar;
      ...  
    }
    
    void  dbg_sighandler(int)
    {
      std::cout << dbg1 << std::endl;
      std::exit(EXIT_FAILURE);
    }
    
    $> while [ $? -eq 0 ]
       do
       ./my_program
       done