围绕此问题进行了很多讨论,每个人都同意您应该始终调用Delegate.EndInvoke来防止内存泄漏(甚至Jon Skeet都说过!)。
我始终遵循本指南,没有提出质疑,但是最近我实现了自己的AsyncResult类,并且看到唯一可能泄漏的资源是AsyncWaitHandle。
(实际上,它实际上并没有泄漏,因为WaitHandle使用的本机资源封装在具有终结器的SafeHandle中,尽管这样,它将对垃圾收集器的终结队列增加压力。即使如此,AsyncResult的良好实现也将仅按需初始化AsyncWaitHandle ...)
知道是否有泄漏的最好方法就是尝试一下:
Action a = delegate { };
while (true)
a.BeginInvoke(null, null);
我运行了一段时间,内存停留在9-20 MB之间。
让我们与调用Delegate.EndInvoke的时间进行比较:
Action a = delegate { };
while (true)
a.BeginInvoke(ar => a.EndInvoke(ar), null);
通过此测试,内存在9-30 MG之间播放,很奇怪吗? (可能是因为当存在AsyncCallback时执行时间会更长一些,因此ThreadPool中将有更多排队的委托)
您如何看待……“神话破灭”?
附言ThreadPool.QueueUserWorkItem比Delegate.BeginInvoke效率高一百倍,最好将它用于触发和忘记呼叫。
最佳答案
我确实进行了一些测试,以调用Action委托并在其中引发异常。然后,确保通过一次仅维护指定数量的线程来运行,并确保在不删除线程结束时连续填充线程池,从而确保不会淹没线程池。这是代码:
static void Main(string[] args)
{
const int width = 2;
int currentWidth = 0;
int totalCalls = 0;
Action acc = () =>
{
try
{
Interlocked.Increment(ref totalCalls);
Interlocked.Increment(ref currentWidth);
throw new InvalidCastException("test Exception");
}
finally
{
Interlocked.Decrement(ref currentWidth);
}
};
while (true)
{
if (currentWidth < width)
{
for(int i=0;i<width;i++)
acc.BeginInvoke(null, null);
}
if (totalCalls % 1000 == 0)
Console.WriteLine("called {0:N}", totalCalls);
}
}
在运行了大约20分钟并进行了超过3000万次BeginInvoke调用之后,专用字节内存消耗保持不变(23 MB)以及Handle计数。似乎没有泄漏。我已经通过CLR阅读了Jeffry Richters的C#书,其中他指出存在内存泄漏。至少对于.NET 3.5 SP1似乎不再如此。
测试环境:
Windows 7 x86
.NET 3.5 SP1
英特尔6600双核2.4 GHz
你的
阿洛伊斯·克劳斯(Alois Kraus)
关于.net - 不调用Delegate.EndInvoke会导致内存泄漏……是神话吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1774202/