不调用Delegate.EndInvoke会导致内存泄漏...一个神话?

Jef*_*Cyr 14 .net memory-leaks asynchronous

围绕这个进行了很多讨论,每个人都倾向于同意你应该总是调用Delegate.EndInvoke来防止内存泄漏(甚至Jon Skeet说过它!).

我总是毫不疑问地遵循这个准则,但最近我实现了自己的AsyncResult类,并发现可能泄漏的唯一资源是AsyncWaitHandle.

(事实上​​它并没有真正泄漏,因为WaitHandle使用的本机资源被封装在一个具有Finalizer的SafeHandle中,但它会对垃圾收集器的finalize队列施加压力.即便如此,AsyncResult的良好实现也会只按需初始化AsyncWaitHandle ...)

了解是否存在泄漏的最佳方法就是尝试:

Action a = delegate { };
while (true)
    a.BeginInvoke(null, null);
Run Code Online (Sandbox Code Playgroud)

我运行了一段时间,内存保持在9-20 MB之间.

让我们与调用Delegate.EndInvoke时进行比较:

Action a = delegate { };
while (true)
    a.BeginInvoke(ar => a.EndInvoke(ar), null);
Run Code Online (Sandbox Code Playgroud)

通过这个测试,内存在9-30 MG之间播放,怪异的呃?(可能是因为当存在AsyncCallback时执行需要更长的时间,因此ThreadPool中将有更多的排队委托)

你觉得怎么样......"神话被破坏了"?

PS ThreadPool.QueueUserWorkItem比Delegate.BeginInvoke效率高一百,它更适合用于fire&forget调用.

Eri*_*sch 11

它当前是否泄漏内存不是你应该依赖的东西.未来,框架团队可能会以一种可能导致泄密的方式改变事物,而且由于官方政策是"你必须调用EndInvoke",那么它就是"按设计".

你是否真的希望你的应用程序在未来的某个时间突然开始泄漏内存,因为你选择依靠观察到的行为而不是记录的要求?

  • +1.此外,没有必要推测未来修订框架的危险性.正如我在回答中指出的那样,不考虑内存泄漏的可能性,不调用`Delegate.EndInvoke`将导致异常被忽略,并且无法知道动作是否成功完成. (2认同)

Alo*_*aus 6

我做了一个小测试来调用一个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);

    }
}
Run Code Online (Sandbox Code Playgroud)

让它运行大约20分钟后,超过3000万个BeginInvoke调用以后私有字节内存消耗是恒定的(23 MB)以及句柄计数.似乎没有泄漏存在.我已经通过CLR阅读了Jeffry Richters的C#书,其中他说存在内存泄漏.对于.NET 3.5 SP1,至少这似乎不再适用.

测试环境:Windows 7 x86 .NET 3.5 SP1 Intel 6600双核2.4 GHz

你的,Alois Kraus