C++ 如何在C++;哪个是挂起的(死锁)?

C++ 如何在C++;哪个是挂起的(死锁)?,c++,multithreading,debugging,C++,Multithreading,Debugging,在java中调试挂起的应用程序很容易。您可以获取应用程序的内存转储,并使用eclipse jvm转储分析器查看线程的状态以及每个线程被阻塞的位置 这种情况是否存在于C++? 一些平台支持。我没有这样做,但是我认为您可以使用GDB在挂起的时候生成应用程序的核心。 您可以尝试使用gdb本身调试这个核心,并亲自查看哪些线程在哪里被阻止 在linux平台上,上述操作是可能的。不确定windows上的cygwin是否可以用于相同的目的。当然,策略性地放置的cout语句(或其他输出选项)始终是一种选择,但往

在java中调试挂起的应用程序很容易。您可以获取应用程序的内存转储,并使用eclipse jvm转储分析器查看线程的状态以及每个线程被阻塞的位置


这种情况是否存在于C++?

一些平台支持。

我没有这样做,但是我认为您可以使用GDB在挂起的时候生成应用程序的核心。 您可以尝试使用gdb本身调试这个核心,并亲自查看哪些线程在哪里被阻止


在linux平台上,上述操作是可能的。不确定windows上的cygwin是否可以用于相同的目的。

当然,策略性地放置的
cout
语句(或其他输出选项)始终是一种选择,但往往远远不够理想

如果使用g++编译,请使用
-g
编译并使用gdb。您可以附加到正在运行的进程(和源代码),或者只需在调试器中运行程序即可。然后看看堆栈

在Windows中,只需暂停程序的执行并查看堆栈即可

  • 找出被卡螺纹#1所拥有的临界截面
  • 计算卡死螺纹2的临界截面
  • 确定所述临界截面采集的正确顺序

  • <>你可以用C++做完全相同的事情;强制堆芯转储并在之后查看


    或者,如果您使用的是MSVC,只需在应用程序运行时将调试器附加到应用程序即可。点击“全部中断”并浏览线程。

    您可以在Linux系统上使用gdb查看线程状态。

    gdb中的神奇调用是:

    所有线程都应用bt

    为所有线程运行bt(backtrace)命令的。除非您完全剥离了程序,否则您应该能够看到每个函数的名称


    这适用于实时和事后调试(即针对核心运行gdb)。

    在Windows本机应用程序中,Windbg是我的首选工具。如果可能的话,我会实时调试一个死锁的进程,失败的话,一个完整的进程内存转储通常会让你到达那里

    我的方法是绘制一个文档,记录线程和资源之间的关系。我通常从运行命令开始,以确定哪些线程持有死锁进程中的任何关键部分

    然后,我通过选择争用计数最高的关键部分开始绘制等待图(如果出现死锁,那么图中会有一个循环,因此从何处开始并不重要)。找到所属线程并在调试器中选择它(使用~命令,您可以将线程ID与调试器使用的线程编号相关联,使用~***threadnumber***s选择线程,并使用kbn显示其堆栈。如果进程死锁,则可能会执行某种阻塞操作,例如查找对Rtl的调用输入CriticalSection或WaitForSingleObject等。在死锁情况下,这些调用通常使您能够识别正在等待的另一个资源。将此信息添加到等待图中,然后继续,直到返回到开始的位置

    如果您的等待图跨越了进程边界,您可能会发现您需要找到在另一个进程中拥有内核对象的人(这就是为什么我可以实时调试的原因)。sysinternals process Explorer工具可用于此目的

    一旦你确定了陷入僵局的参与者,那么你就需要考虑下一步该怎么做。这可能意味着改变资源获取的顺序(正如有人指出的)但实际上没有一种通用的方法,它需要关于应用程序设计的额外信息来理解如何删除等待图中的循环依赖关系

    在某些情况下,周期可能不是问题的原因,例如,您的系统可能正在等待永远不会出现的用户输入(任何看到调用MessageBox以获取作为服务运行的进程的人,请举手)


    当然,还有比这更多的内容,但我希望这可能会让您朝着正确的方向前进。

    我们可以使用下面的gdb命令来调试死锁

    • 使用以下命令附加到处于挂起/死锁状态的正在运行的进程

      gdb-p

    • 连接到该进程后,可以使用下面的命令查看所有LWP

    (gdb)信息线程

    Id   Target Id         Frame 
    
    16   Thread 0xfff06111f0 (LWP 2791) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    15   Thread 0xffefdf01f0 (LWP 2792) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    14   Thread 0xffef5bb1f0 (LWP 2793) "abc.d" 0x000000fff26feb4c in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    
    13   Thread 0xffeed351f0 (LWP 2794) "abc.d" 0x000000fff2703924 in nanosleep () from /lib64/libpthread.so.0
    
    12   Thread 0xffee5351f0 (LWP 2795) "abc.d" 0x000000fff26fe76c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    
    11   Thread 0xffec8a71f0 (LWP 2796) "abc.d" 0x000000fff26fe76c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    
    10   Thread 0xffd7cd11f0 (LWP 2797) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    9    Thread 0xffd74d11f0 (LWP 2798) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    8    Thread 0xffd6cd11f0 (LWP 2801) "abc.d" 0x000000fff27022f4 in __lll_lock_wait () from /lib64/libpthread.so.0
    
    7    Thread 0xffd64d11f0 (LWP 2802) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    6    Thread 0xffd5cd11f0 (LWP 2803) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    5    Thread 0xffd54d11f0 (LWP 2804) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    4    Thread 0xffd4cd11f0 (LWP 2805) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    3    Thread 0xffc7fff1f0 (LWP 2928) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    2    Thread 0xffc77ff1f0 (LWP 2929) "abc.d" 0x000000fff0f0104c in select () from /lib64/libc.so.6
    
    1    Thread 0xfff0a62000 (LWP 2744 for) "abc.d" 0x000000fff0f19b9c in __lll_lock_wait_private () from /lib64/libc.so.6
    
    • 我们可以看到线程1和线程8处于等待状态,我们可以转到每个线程,如下所示

      (gdb)线程1

      (gdb)bt

    上述命令的输出如下:

    (gdb) thread 1 
    
    [Switching to thread 1 (Thread 0xfff0a62000 (LWP 2744))]
    
    0  0x000000fff0f19b9c in __lll_lock_wait_private () from /lib64/libc.so.6
    
    (gdb) bt 
    
    0  0x000000fff0f19b9c in __lll_lock_wait_private () from
    /lib64/libc.so.6
    
    1  0x000000fff0ea3238 in malloc () from /lib64/libc.so.6
    
    2  0x000000fff115df0c in operator new(unsigned long) ()    from
    /lib64/libstdc++.so.6
    
    3  0x000000fff11ceddc in std::string::_Rep::_S_create(unsigned long,
    unsigned long, std::allocator<char> const&) () from
    /lib64/libstdc++.so.6
    
    4  0x000000fff11d165c in char* std::string::_S_construct<char
    const*>(char const*, char const*, std::allocator<char> const&,
    std::forward_iterator_tag) ()    from /lib64/libstdc++.so.6
    
    5  0x000000fff11d1760 in std::basic_string<char,
    std::char_traits<char>, std::allocator<char> >::basic_string(char
    const*, std::allocator<char> const&) ()    from /lib64/libstdc++.so.6
    
    6  0x000000fff1eeac1c in getTime() ()    from
    /usr/sbin/dir/sharedobj/liblibLite.so
    
    7  0x000000fff1eeb18c in Logging::logBegin() ()    from
    /usr/sbin/dir/sharedobj/liblibLite.so
    
    8  0x000000fff1f324f8 in sigsegv_handler(int, siginfo_t*, void*) ()   
    from /usr/sbin/dir/sharedobj/liblibLite.so
    
    9  signal handler called
    
    10 0x000000fff0e9f530 in malloc_consolidate () from /lib64/libc.so.6
    
    11 0x000000fff0ea0160 in _int_free () from /lib64/libc.so.6
    
    12 0x000000fff115b184 in operator delete(void*) () from
     /lib64/libstdc++.so.6
    
    13 0x000000fff115b1f4 in operator delete[](void*) ()    from
     /lib64/libstdc++.so.6
    
    14 0x000000fff20cfd60 in pstream::~pstream() ()    from
     /usr/sbin/dir/sharedobj/libconnV2.so
    
    15 0x000000fff208ffd8 in ifaceSocket::dispatchMsg(pstream&) ()    from
     /usr/sbin/dir/sharedobj/libsockIf.so
    
    16 0x000000fff207d5a4 in
     socketInterface::socket_callback(ConnectionEvent, char*, int) () from
     /usr/sbin/dir/sharedobj/libsockIf.so
    
    17 0x000000fff208f43c in ifaceSocket::Callback(ConnectionEvent, char*,
     int)
     () from /usr/sbin/dir/sharedobj/libsockIf.so
    
    18 0x000000fff20c4674 in ConnectionOS::ProcessReadEvent() ()    from
     /usr/sbin/dir/sharedobj/libconnV2.so
    
    19 0x000000fff20cc808 in ConnectionOSManager::ProcessConns(fd_set*,
     fd_set*)
     () from /usr/sbin/dir/sharedobj/libconnV2.so
    
    20 0x000000fff20cf3bc in SocketsManager::ProcessFds(bool) ()    from 
    /usr/sbin/dir/sharedobj/libconnV2.so
    
    21 0x000000fff1e54aa8 in EventReactorBase::IO() ()    from 
     /usr/sbin/dir/sharedobj/libthreadlib.so
    
    22 0x000000fff1e5406c in EventReactorBase::React() ()    from 
    /usr/sbin/dir/sharedobj/libthreadlib.so
    
    23 0x000000fff1e50508 in Task::Run() ()    from 
    /usr/sbin/dir/sharedobj/libthreadlib.so
    
    24 0x000000fff1e50584 in startTask(void*) ()    from 
    /usr/sbin/dir/sharedobj/libthreadlib.so
    
    25 0x00000000104a421c in TaskMgr::Start() ()
    
    26 0x00000000100ddddc in main ()
    

    您正在开发什么环境?(Visual Studio、GNU/Linux)如果您能同时讨论这两种环境,那就太好了。在这两种环境中调试都很有趣,也是一项挑战。由于Windows上无法自动获取内核,因此在Windows中调试更具挑战性。人们可能会有其他观点。堆栈肯定是一种选择,我经常会错过。谢谢提醒。:)我会说,我给出的答案或多或少是正确的。MSVC确实提供了一个非常简单的调试C++应用程序的接口。你不妨告诉他不要先写bug。对不起,如果我很短,但那确实是所有的。死锁中的所有玩家都被困在犯罪现场。现在,如果他多SPE的话。关于平台,我可以就如何做提供更好的建议#1-2。。。
     (gdb) info reg
     From r8 field get the very first address    
    
     (gdb) print *((int*)(0x0000000019ff3d30))
     $1 = 2 // Locks    
     (gdb) print *((int*)(0x0000000019ff3d30)+1)
     $2 = 0 // Count        
     (gdb) print *((int*)(0x0000000019ff3d30)+2)
     $3 = 2744 // Owner PID