我写了一个程序,每1秒调用ThreadPool.QueueUserWorkItem
。但是,它会导致大量消耗事件句柄。
class Program
{
private static Timer _timer;
static void Main(string[] args)
{
_timer = new Timer(Run, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
_timer.Change(new TimeSpan(0, 0, 0, 1), Timeout.InfiniteTimeSpan);
Console.ReadLine();
_timer.Dispose();
}
static void Run(object state)
{
ThreadPool.QueueUserWorkItem((object statex) =>
{
int s = 0;
for (int i = 0; i < 1000; ++i)
{
int j = i*i;
s += j;
}
Console.WriteLine(s);
});
_timer.Change(new TimeSpan(0, 0, 0, 1), Timeout.InfiniteTimeSpan);
}
}
通过使用windbg的!htrace -diff
命令,我发现在两个快照之间创建了很多事件句柄,而!htrace -diff
确保所有这些句柄都没有关闭。句柄如下:
Handle = 0x00000000000003d0 - OPEN
Thread ID = 0x00000000000046a0, Process ID = 0x0000000000003f50
0x000007fd7932306a: ntdll!ZwCreateEvent+0x000000000000000a
0x000007fd766126cf: KERNELBASE!CreateEventW+0x0000000000000063
0x000007fd6ee0d647: clr!CLREventBase::CreateManualEvent+0x0000000000000028
0x000007fd6ee0e33f: clr!Thread::AllocHandles+0x000000000000005f
0x000007fd6ee0f231: clr!Thread::CreateNewOSThread+0x0000000000000091
0x000007fd6ee0f152: clr!Thread::CreateNewThread+0x00000000000000ae
0x000007fd6ee0f9b1: clr!ThreadpoolMgr::CreateUnimpersonatedThread+0x00000000000000c5
0x000007fd6ee0cccc: clr!ThreadpoolMgr::CreateWorkerThread+0x0000000000000019
0x000007fd6ee0cca5: clr!ThreadpoolMgr::MaybeAddWorkingWorker+0x000000000000011d
0x000007fd6ee0d398: clr!ManagedPerAppDomainTPCount::SetAppDomainRequestsActive+0x0000000000000024
0x000007fd6ee0d436: clr!ThreadpoolMgr::SetAppDomainRequestsActive+0x000000000000002a
0x000007fd6ee0d3d2: clr!ThreadPoolNative::RequestWorkerThread+0x000000000000002f
0x000007fd6dc062ea: mscorlib_ni+0x00000000005662ea
0x000007fd6db3c53e: mscorlib_ni+0x000000000049c53e
……….
Handle = 0x00000000000003cc - OPEN
Thread ID = 0x00000000000046a0, Process ID = 0x0000000000003f50
0x000007fd7932306a: ntdll!ZwCreateEvent+0x000000000000000a
0x000007fd766126cf: KERNELBASE!CreateEventW+0x0000000000000063
0x000007fd6ee0d647: clr!CLREventBase::CreateManualEvent+0x0000000000000028
0x000007fd6ee0e331: clr!Thread::AllocHandles+0x0000000000000051
0x000007fd6ee0f231: clr!Thread::CreateNewOSThread+0x0000000000000091
0x000007fd6ee0f152: clr!Thread::CreateNewThread+0x00000000000000ae
0x000007fd6ee0f9b1: clr!ThreadpoolMgr::CreateUnimpersonatedThread+0x00000000000000c5
0x000007fd6ee0cccc: clr!ThreadpoolMgr::CreateWorkerThread+0x0000000000000019
0x000007fd6ee0cca5: clr!ThreadpoolMgr::MaybeAddWorkingWorker+0x000000000000011d
0x000007fd6ee0d398: clr!ManagedPerAppDomainTPCount::SetAppDomainRequestsActive+0x0000000000000024
0x000007fd6ee0d436: clr!ThreadpoolMgr::SetAppDomainRequestsActive+0x000000000000002a
0x000007fd6ee0d3d2: clr!ThreadPoolNative::RequestWorkerThread+0x000000000000002f
0x000007fd6dc062ea: mscorlib_ni+0x00000000005662ea
0x000007fd6db3c53e: mscorlib_ni+0x000000000049c53e
通过使用!handle
,我找到了124个事件句柄:
187 Handles
Type Count
None 3
Event 124
Section 4
File 7
Directory 3
Mutant 4
WindowStation 2
Semaphore 2
Key 10
Thread 21
Desktop 1
IoCompletion 2
Timer 3
TpWorkerFactory 1
所以,我真的很想知道为什么QueueUserWorkItem
会创建如此多的事件句柄???这是一个错误还是CLR的设计?
在我们的制作服务上,由于我们使用async
操作很多,基本上每个async
操作都会调用CLR的QueueUserWorkItem
。我认为这就是为什么我们的服务有超过60000个句柄的原因。但是,如果我真的想减少手柄的数量呢?这么多句柄在内核内存中会有很多对象,我真的想减少内存使用量。