如何使用 WinDBG 临界区和线程列表查找线程 ID?

How can I find thread ID using WinDBG critical section and thread list?

我分析了转储文件以找到锁定的线程,并且我使用 !cs 命令找到了。

DebugInfo          = 0x00639610
Critical section   = 0x03e210c8 (+0x3E210C8)
LOCKED
LockCount          = 0x1
WaiterWoken        = No
OwningThread       = 0x000017c8
RecursionCount     = 0x1
LockSemaphore      = 0x5B0
SpinCount          = 0x00000000

这个结果意味着ThreadID 0x000017c8获得了临界区。所以我列出了所有线程以使用 ~ 命令查找 threadID 0x000017c8。

0:000> ~
.  0  Id: 2240.1758 Suspend: 0 Teb: 7efdd000 Unfrozen
   1  Id: 2240.1d90 Suspend: 0 Teb: 7efda000 Unfrozen
   2  Id: 2240.16fc Suspend: 0 Teb: 7efd7000 Unfrozen
   3  Id: 2240.1544 Suspend: 0 Teb: 7ef9c000 Unfrozen
   4  Id: 2240.2550 Suspend: 0 Teb: 7ef99000 Unfrozen
   5  Id: 2240.fd4 Suspend: 0 Teb: 7ef96000 Unfrozen
   6  Id: 2240.1b08 Suspend: 0 Teb: 7ef93000 Unfrozen
   7  Id: 2240.1958 Suspend: 0 Teb: 7ef90000 Unfrozen
   8  Id: 2240.20e8 Suspend: 0 Teb: 7ef8d000 Unfrozen
   9  Id: 2240.1bec Suspend: 0 Teb: 7ef8a000 Unfrozen
  10  Id: 2240.fb4 Suspend: 0 Teb: 7ef87000 Unfrozen
  11  Id: 2240.25c0 Suspend: 0 Teb: 7ef84000 Unfrozen
  12  Id: 2240.15b0 Suspend: 0 Teb: 7ef81000 Unfrozen
  13  Id: 2240.21a8 Suspend: 0 Teb: 7ef7b000 Unfrozen
  14  Id: 2240.1fcc Suspend: 0 Teb: 7ef78000 Unfrozen

但是,没有threadID 0x000017c8。这个转储文件发生了什么?我如何找到线程 ID 0x000017c8?

Kjell Gunnar 是对的:线程 17c8 已因任何原因终止。

这不仅会发生在临界区,也会发生在其他同步对象上。在我的高级调试培训中,我为参与者提供了一个 ManualResetEvent.

具有类似情况的转储

来源如下:

#include "stdafx.h"
#include <Windows.h>
#include <process.h>
#include <stdio.h>
#include <iostream>


HANDLE threadA;
HANDLE threadB;
HANDLE eventB;

class WorkItem
{
public:
    virtual void Initialize()
    {
    }
};

unsigned int __stdcall initializeWorkitems(void* param) 
{
    try
    {
        // Initialize workitems
        WorkItem **items = new WorkItem*[2];
        items[0] = new WorkItem();
        items[1] = NULL;

        for (int i = 0; i<2; i++)
        {
                items[i]->Initialize();
        }

        // Signal event for second thread to work on work items
        SetEvent(eventB);
    }
    catch(...)
    {
        // Don't do this
    }
    return 0;
}

unsigned int __stdcall processWorkitems(void* param) 
{
    // Wait for work item initialization to complete
    WaitForSingleObject(eventB, INFINITE);

    // Work on workitems
    Sleep(100);

    return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
    eventB = CreateEvent(0, 0, 0, 0);

    threadA = (HANDLE)_beginthreadex(0, 0, &initializeWorkitems, (void*)0, 0, 0);
    threadB = (HANDLE)_beginthreadex(0, 0, &processWorkitems, (void*)0, 0, 0);

    WaitForSingleObject(threadA, INFINITE);
    WaitForSingleObject(threadB, INFINITE);

    CloseHandle(threadA);
    CloseHandle(threadB);

    CloseHandle(eventB);

    return 0;
}

在进程挂起时创建转储。分析的结果应该与您的关键部分相当。 threadA 终止静默,在 catch(...) 块中吃掉 NullPointerException,这样就永远不会释放手动重置事件。