0

我想使用这个解决方案来调用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()但我不希望分支EndInvoke中的阻塞调用。else不知何故,我们需要abort运行Console.ReadLine()调用而不是让它运行到完成,因为它可能永远不会完成。

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

我们如何才能使它正常工作,而不会遇到资源泄漏?

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

4

1 回答 1

0

如前所述,在阅读了关于几个类似问题的大量评论后,我开始相信这里没有真正的解决方案。微软的方式Begin/EndInvoke

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

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

警告:

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

在我上面的帖子顶部的原始问题上接受的解决方案使用第二个线程和计时方法。但是,它不会终止同步调用,而是保持其运行,因为后续异步调用需要它,这是一个很好的技巧。

使用第二个线程的代码实际上很简单:

    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上述已接受的解决方案中稍微复杂的方法。

于 2015-03-04T16:57:05.340 回答