繁体   English   中英

如何通过丢失EndInvoke调用来修复资源泄漏?

[英]How to fix resource leak because of missing EndInvoke call?

我想使用此解决方案以超时方式调用Console.ReadLine()

delegate string ReadLineDelegate();

string ReadLine(int timeoutms)
{
    string resultstr = null;
    ReadLineDelegate d = Console.ReadLine;
    IAsyncResult result = d.BeginInvoke(null, null);
    result.AsyncWaitHandle.WaitOne(timeoutms);//timeout e.g. 15000 for 15 secs
    if (result.IsCompleted)
    {
        resultstr = d.EndInvoke(result);
        Console.WriteLine("Read: " + resultstr);        
    }
    else
    {
        Console.WriteLine("Timed out!");
        // Bug? resource leak? No d.EndInvoke(), which blocks until Console.ReadLine() returns
    }
    result.AsyncWaitHandle.Close();
    return resultstr;
}

但评论者警告说:

every ReadLine you call sits there waiting for input. 
If you call it 100 times, it creates 100 threads 
which don't all go away until you hit Enter 100 times!

...特别是因为我想在永久循环中反复调用它。

我知道每个BeginInvoke()需要一个EndInvoke()但我不希望在else分支中阻塞EndInvoke调用。 不知何故,我们需要abort正在运行的Console.ReadLine()调用,而不是让它运行完成,因为它可能永远不会完成。

所以这些(复杂的)代码帮助我让Console.ReadLine在超时时返回,但不会结束Console.ReadLine以退出或以其他方式消失。

如何在不遇到资源泄漏的情况下使其正常工作?

注意:我按照MS Calling Sync调用的建议异步添加了AsyncWaitHandle.Close()

在阅读了如上所述的几个类似问题的大量评论之后,我开始相信这里没有真正的解决方案。 使用Begin/EndInvoke的Microsoft方法是

  • 相当复杂,并且:
  • 不够

一种更直接的方法是在另一个线程中运行同步调用,使用计时方法来跟踪超时,并使用Thread.Abort()来消除超时同步调用。

警告:

同步调用可能支持也可能不支持中止。 例如, Console.ReadLine()将被中止,但是如果重新启动线程,则不再从控制台读取数据。

在我上面发布的原始问题上接受的解决方案使用第二个线程和计时方法。 但是,它不会终止同步调用但会保持运行,因为后续异步调用需要它,这是一个很好的黑客攻击。

使用第二个线程的代码实际上是直截了当的:

    public class MySyncProc
    {
        /// <summary>
        /// Value returned from the process after completion
        /// </summary>
        public string Result = null;

        ...other shared data...

        public MySyncProc() { }

        public void Run()
        {
            Result = LengthyProcess(...);
            return;
        }
    }

    public string Run(int TimeoutMs)
    {
        MySyncProc SyncP = new MySyncProc() { arg1 = ..., etc };
        //
        Thread T = new Thread(SyncP.Run);
        T.Start();
        //
        DateTime StopTime = DateTime.Now.AddMilliseconds(TimeoutMs);
        while (DateTime.Now < StopTime && SyncP.Result == null)
        {
            Thread.Sleep(200);
        }
        if (T.IsAlive)
        {
            T.Abort();
            Console.WriteLine("Aborted thread for: {0}", Name);
        }
        return SyncP.Result;
    }

如果您不喜欢轮询,请使用稍微复杂的AutoResetEvent如上述接受的解决方案中所述。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM