Как сообщить об истечении времени ожидания в асинхронном вызове? - PullRequest
0 голосов
/ 09 октября 2009

Я изучаю многопоточность. Моя цель - передать некоторые значения методу для расчета, если результат не будет возвращен в течение 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»

Какой риск связан с памятью? Вы можете привести пример?

Ответы [ 2 ]

2 голосов
/ 09 октября 2009

Ответы

(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;
    }
1 голос
/ 09 октября 2009

Из значения тайм-аута, да, это будет установить тайм-аут, как вы ожидаете.

Что касается риска памяти. Существует риск того, что если вы не вызовете EndInvoke, все равно будет ссылка на делегат, и любые использованные ресурсы могут не быть собраны сборщиком мусора до выхода из приложения. Точная реализация не на 100% документирована из того, что я нашел, но может быть подтверждено с помощью такого инструмента, как ANTS Profiler.

Вот полезное обсуждение .

...