0

我正在学习线程。我的意图是将一些值传递给一个计算方法,如果在 20 毫秒内没有返回结果,我将报告“操作超时”。根据我的理解,我实现了如下代码:

public delegate long CalcHandler(int a, int b, int c);


public static void ReportTimeout()
    {
        CalcHandler doCalculation = Calculate;

        IAsyncResult asyncResult = 
        doCalculation.BeginInvoke(10,20,30, null, null);

        if (!asyncResult.AsyncWaitHandle.WaitOne(20, false))
        {
            Console.WriteLine("..Operation Timeout...");
        }

        else
       {
        // Obtain the completion data for the asynchronous method.
        long val;
        try
        {
            val=  doCalculation.EndInvoke(asyncResult);
            Console.WriteLine("Calculated Value={0}", val);
        }
        catch
        {
            // Catch the exception
        }
     }

    } 

public static long Calculate(int a,int b,int c)
     {
        int m;
        //for testing timeout,sleep is introduced here.
        Thread.Sleep(200);
        m = a * b * c;
        return m;
    }

问题 :

(1) 报告超时是否正确?

(2) 如果时间到了,我不会调用 EndInvoke() 。在这种情况下调用 EndInvoke() 是强制性的吗?

(3) 我听说

“即使您不想处理异步方法的返回值,您也应该调用 EndInvoke;否则,每次使用 BeginInvoke 启动异步调用时,您都有可能泄漏内存”

与记忆相关的风险是什么?你能举个例子吗?

4

2 回答 2

2

答案

(1) 通常你只是抛出 System.TimeoutException

(2) 需要 EndInvoke() 的原因是,如果返回值(或抛出的异常)有内存分配,GC 可能不会尽快清理它。我还没有看到这是一个大问题的案例......但这只是我。

如果您真的担心它,您可以调用 ThreadPool.QueueUserWorkItem 这主要是上述异步调用将执行的操作。(注意:在 WinForms 或其他线程相关的上下文中可能是一个问题)。无论如何,您可以按如下方式运行自己的工作项:

    public static long Calc(CalcHandler fn, int a, int b, int c)
    {
        return Run<long>(TimeSpan.FromSeconds(20), delegate { return fn(a, b, c); });
    }

    public static T Run<T>(TimeSpan timeout, Func<T> operation)
    {
        Exception error = null;
        T result = default(T);

        ManualResetEvent mre = new ManualResetEvent(false);
        System.Threading.ThreadPool.QueueUserWorkItem(
            delegate(object ignore)
            {
                try { result = operation(); }
                catch (Exception e) { error = e; }
                finally { mre.Set(); }
            }
        );
        if (!mre.WaitOne(timeout, true))
            throw new TimeoutException();
        if (error != null)
            throw new TargetInvocationException(error);
        return result;
    }
于 2009-10-08T22:04:17.573 回答
1

从超时值来看,是的,这将按照您的预期设置超时。

至于内存风险。如果您不调用 EndInvoke,则存在对委托的引用的风险,并且在应用程序退出之前,所使用的任何资源都可能不会被垃圾收集。确切的实现并没有 100% 记录在我发现的内容中,但可以使用 ANTS Profiler 之类的工具进行确认。

这是一个有用的讨论

于 2009-10-08T21:50:06.050 回答