围绕此问题进行了很多讨论,每个人都同意您应该始终调用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/

10-15 06:39