Нужно ли вызывать EndInvoke после тайм-аута? - PullRequest
9 голосов
/ 17 февраля 2009

На веб-странице я звоню третьей стороне, которая не позволяет мне устанавливать таймаут программно. Я вызываю BeginInvoke и использую AsyncWaitHandle.WaitOne, чтобы ждать указанное количество времени.

Если время ожидания истекло, я продолжаю и забываю о вызове потока, который я начал. У меня вопрос, нужно ли мне как-то вызывать EndInvoke в ситуации тайм-аута? Замечание "ВНИМАНИЕ" на этой странице MSDN заставляет меня задуматься, стоит ли мне: http://msdn.microsoft.com/en-us/library/2e08f6yc(VS.71).aspx

Если вы считаете, что я должен, то следующий вопрос заключается в том, что, если моя веб-страница завершит обработку и вернется к клиенту до того, как вернется третья сторона, будет ли вообще использоваться метод обратного вызова для прослушивания кода? Разве сервер не прекращает поиск активности после выполнения моего запроса / ответа?

Вот код, который я использую:

public class RemotePaymentProcessor
{
    private delegate string SendProcessPaymentDelegate(string creditCardNumber);

    private string SendProcessPayment(string creditCardNumber)
    {
        string response = string.Empty;
        // call web service
        SlowResponseService.SlowResponseService srs = new WebServiceTimeout.SlowResponseService.SlowResponseService();
        response = srs.GetSlowResponse(creditCardNumber);
        return response;
    }

    public string ProcessPayment(string creditCardNumber, int timeoutMilliseconds)
    {
        string response = string.Empty;

        SendProcessPaymentDelegate sppd = new SendProcessPaymentDelegate(SendProcessPayment);
        IAsyncResult ar = sppd.BeginInvoke(creditCardNumber, null, new object());
        if (!ar.AsyncWaitHandle.WaitOne(timeoutMilliseconds, false))
        {
            // Async call did not return before timeout
            response = "TIMEOUT";
        }
        else
        {
            // Async call has returned - get response
            response = sppd.EndInvoke(ar);
        }
        return response;
    }
}

Ответы [ 4 ]

2 голосов
/ 17 февраля 2009

Возможно, что если вы не вызовете EndInvoke, вы потеряете некоторый ресурс (выделенный BeginInvoke).

Чтобы быть в полной безопасности, всегда вызывайте EndInvoke () (который будет блокировать, поэтому делайте это в неком фоновом потоке, который вам не нужен, или переключитесь на передачу обратного вызова, чтобы не записывать поток во время ожидания).

На практике я не знаю, как часто это имеет значение (я думаю, что многие AsyncResults не будут течь, так что вы можете повезти и быть в безопасности в этом случае).

Все это почти не имеет ничего общего с запросом-ответом и веб-серверами и так далее, именно так работает модель программирования Begin / End, независимо от того, в каком приложении вы ее используете.

1 голос
/ 17 февраля 2009

Обновление:
Похоже, вам нужно вызывать EndInvoke всегда для асинхронного вызова (если он не Control.BeginInvoke) или риска утечки ресурсов.

Вот обсуждение в том же ключе . Предложенное решение состоит в том, чтобы создать поток, который будет ожидать фактического завершения делегата и вызова EndInvoke. Однако в случае действительно Looong таймаута я думаю, что поток просто зависнет.

Это крайний случай, который, кажется, не документирован так хорошо ... может быть, потому что таймауты не должны происходить .. исключительные случаи

0 голосов
/ 11 марта 2009

Ну, я не мог проигнорировать совет, который я видел везде, что если я не буду вызывать EndInvoke, я МОГУТ утратить некоторые ресурсы, поэтому мне нужно было попытаться заснуть это ночью и не волноваться, что я был приближается к утесу.

Решение, которое я нашел, использовало функцию асинхронного обратного вызова. Если звонок вернулся вовремя, я вызываю EndInvoke там. Если нет, я продолжаю щелкать по кнопке и позволяю асинхронной функции обратного вызова убирать беспорядок с EndInvoke.

Чтобы ответить на мой собственный вопрос о веб-приложении и «будет ли кто-нибудь, кто будет слушать после того, как я перестану работать и двигаться дальше», я обнаружил, что они будут - даже если бы я остановился и перешел, если я смотрел более поздний вывод , этот асинхронный вызов вернет и запустит функцию обратного вызова, даже если я уже вернул вывод клиенту.

Я использовал кое-что из того, что нашел по адресу: http://www.eggheadcafe.com/tutorials/aspnet/847c94bf-4b8d-4a66-9ae5-5b61f049019f/basics-make-any-method-c.aspx

... а также в сочетании с вещами обратного вызова, которые я нашел в другом месте. Вот небольшая примерная функция того, что я сделал ниже. Он сочетает в себе то, что я нашел в Спасибо за вклад каждого!

public class RemotePaymentProcessor
{    
    string currentResponse = string.Empty;

    private delegate string SendProcessPaymentDelegate(string creditCardNumber);    
    private string SendProcessPayment(string creditCardNumber)    
    {        
        SlowResponseService.SlowResponseService srs = new WebServiceTimeout.SlowResponseService.SlowResponseService();        
        string response = srs.GetSlowResponse(creditCardNumber);        
        return response;    
    }    

    public string ProcessPayment(string creditCardNumber, int timeoutMilliseconds)    
    {        
        string response = string.Empty;        
        SendProcessPaymentDelegate sppd = new SendProcessPaymentDelegate(SendProcessPayment);        
        IAsyncResult ar = sppd.BeginInvoke(creditCardNumber,  new AsyncCallback(TransactionReturned), sppd);        
        if (!ar.AsyncWaitHandle.WaitOne(timeoutMilliseconds, false))        
        {            
            // Async call did not return before timeout            
            response = "TIMEOUT";        
        }        
        else            
        {            
            // Async call has returned - get response            
            response = sppd.EndInvoke(ar);        
        }        

        currentResponse = response; // Set class variable
        return response;    
    }

    private void TransactionReturned(IAsyncResult ar)
    {
        string response = string.Empty;

        // Get delegate back out of Async object
        SendProcessPaymentDelegate sppd = (SendProcessPaymentDelegate)ar.AsyncState;

        // Check outer class response status to see if call has already timed out
        if(currentResponse.ToUpper().Equals("TIMEOUT"))
        {
            // EndInvoke has not yet been called so call it here, but do nothing with result
            response = sppd.EndInvoke(ar);
        }
        else
        {
            // Transaction must have returned on time and EndInvoke has already been called.  Do nothing.
        }       

    }
}
0 голосов
/ 17 февраля 2009

В общем случае асинхронного шаблона .NET вызов EndXXX, когда вы не хотите завершать операцию, начатую с BeingXXX, будет ошибкой, поскольку, если EndXXX вызывается до завершения операции, он должен блокироваться до ее завершения. Что не очень помогает для тайм-аута.

Определенный API может отличаться (например, WinForms явно не требует EndInvoke).

См. §9.2 «Руководства по проектированию структуры» (2-е издание). Или MSDN .

...