我正在尝试将句柄泄漏检测添加到我的代码的单元测试框架中。(Windows 7、x64 VS2010)
我基本上在每个单元测试之前和之后调用 GetProcessHandleCount() 。这工作正常,除非在测试中创建/销毁线程。
似乎 Windows 偶尔会在线程关闭时创建 1-3 个事件。在循环中运行相同的测试不会增加事件创建计数。(例如,在循环中运行 5000 次测试只会导致创建 1-3 个额外事件)
我不会在自己的代码中手动创建事件。
这似乎与这个问题类似: boost::thread 导致小事件句柄泄漏? 但我正在手动创建/关闭线程。
我遵循了这段代码:http: //blogs.technet.com/b/yongrhee/archive/2011/12/19/how-to-troubleshoot-a-handle-leak.aspx
并从 WinDbg 得到这个调用栈:
Outstanding handles opened since the previous snapshot:
--------------------------------------
Handle = 0x0000000000000108 - OPEN
Thread ID = 0x00000000000030dc, Process ID = 0x0000000000000c90
0x000000007715173a: ntdll!NtCreateEvent+0x000000000000000a
0x0000000077133f26: ntdll!RtlpCreateCriticalSectionSem+0x0000000000000026
0x0000000077133ee3: ntdll!RtlpWaitOnCriticalSection+0x000000000000014e
0x000000007714e40b: ntdll!RtlEnterCriticalSection+0x00000000000000d1
0x0000000077146ad2: ntdll!LdrShutdownThread+0x0000000000000072
0x0000000077146978: ntdll!RtlExitUserThread+0x0000000000000038
0x0000000076ef59f5: kernel32!BaseThreadInitThunk+0x0000000000000015
0x000000007712c541: ntdll!RtlUserThreadStart+0x000000000000001d
--------------------------------------
如您所见,这是在线程关闭时创建的事件。
有没有更好的方法来处理单元测试中的泄漏检测?我目前唯一的选择是:
- 忘记尝试执行此处理泄漏检测
- 启动一些虚拟任务以尝试创建这些虚假事件。
- 在泄漏中允许一些小的公差值并运行每个测试 100 次(因此实际泄漏会很大)
- 获取不包括事件的句柄计数(代码量困难)
我也尝试过在 VS2013 中切换到使用 std::thread,但似乎它在使用时会创建很多后台线程和句柄。(使计数差异更糟)
这是一个自包含的示例,其中 99% 以上的时间(在我的计算机上)在幕后创建了一个事件。(句柄数不同)。将启动/关闭代码放在循环中表明它不会直接泄漏,而是会累积偶尔发生的事件:
#include "stdio.h"
#include <Windows.h>
#include <process.h>
#define THREADCOUNT 3
static HANDLE s_semCommand, s_semRender;
static unsigned __stdcall ExecutiveThread(void *)
{
WaitForSingleObject(s_semCommand, INFINITE);
ReleaseSemaphore(s_semRender, THREADCOUNT - 1, NULL);
return 0;
}
static unsigned __stdcall WorkerThread(void *)
{
WaitForSingleObject(s_semRender, INFINITE);
return 0;
}
int main(int argc, char* argv[])
{
DWORD oldHandleCount = 0;
GetProcessHandleCount(GetCurrentProcess(), &oldHandleCount);
s_semCommand = CreateSemaphoreA(NULL, 0, 0xFFFF, NULL);
s_semRender = CreateSemaphoreA(NULL, 0, 0xFFFF, NULL);
// Spool threads up
HANDLE threads[THREADCOUNT];
for (int i = 0; i < THREADCOUNT; i++)
{
threads[i] = (HANDLE)_beginthreadex(NULL, 4096, (i==0) ? ExecutiveThread : WorkerThread, NULL, 0, NULL);
}
// Signal shutdown - Wait for threads and close semaphores
ReleaseSemaphore(s_semCommand, 1, NULL);
for (int i = 0; i < THREADCOUNT; i++)
{
WaitForSingleObject(threads[i], INFINITE);
CloseHandle(threads[i]);
}
CloseHandle(s_semCommand);
CloseHandle(s_semRender);
DWORD newHandleCount = 0;
GetProcessHandleCount(GetCurrentProcess(), &newHandleCount);
printf("Handle %d -> %d", oldHandleCount, newHandleCount);
return 0;
}