在超时后,我需要调用EndInvoke吗?

9
在一个网页中,我调用了一个不允许我通过程序设置超时的第三方。我调用BeginInvoke并使用AsyncWaitHandle.WaitOne来等待指定的时间。
如果调用超时,我会继续进行并忘记我开始的线程调用。我的问题是,在超时情况下,我是否仍然需要以某种方式调用EndInvoke?这个MSDN页面上的“CAUTION”备注让我想知道是否应该: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

如果您不调用EndInvoke,您可能会泄漏一些资源(由BeginInvoke分配)。

因此,为了绝对安全,请始终调用EndInvoke(它将阻塞,因此请在您不需要的某个后台线程上执行此操作,或者切换到传递回调以避免在等待时消耗线程)。

实际上,我不知道这有多重要(我认为许多AsyncResult不会泄漏,因此您可能会在这种情况下幸运并且是安全的)。

所有这些几乎与请求响应、Web 服务器之类的事情无关,这只是Begin/End编程模型的工作方式,无论您在何种应用程序中使用它。


关于请求-响应方面:如果我传递了一个回调方法,但同时完成了我的按钮点击函数并返回给客户端,那么当BeginInvoke线程返回时是否有任何人在监听?在本地应用程序中,如果Main()完成并关闭了应用程序,则回调永远不会被触发。 - Chad

1

更新:
似乎你需要在异步调用中始终调用EndInvoke(除非是Control.BeginInvoke),否则会存在资源泄漏的风险。

这里有一篇相关讨论。建议的解决方案是启动一个线程来等待委托实际完成并调用EndInvoke。但如果遇到非常长的超时,我认为该线程会挂起。

这是一个边缘情况,并没有很好地记录...也许是因为超时不应该发生...这是特殊情况。


0

嗯,我不能忽略到处都看到的建议,如果我不确定调用EndInvoke,我可能会泄漏一些资源,所以我必须试着让自己安心入眠,不用担心我接近悬崖。

我找到的解决方案使用了异步回调函数。如果调用及时返回,我就在那里调用EndInvoke。如果没有,我就继续我的按钮点击,并让异步回调函数清理EndInvoke的混乱。

为了回答我自己关于Web应用程序和“超时后是否有人会听”,我发现即使我已经超时并移动了位置,如果我观察后来的输出,那个异步调用将返回并运行回调函数,即使我已经向客户端返回了输出。

我使用了一些在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

对于.NET异步模式的一般情况,如果您不想完成使用BeginXXX开始的操作,则调用EndXXX将是一个错误,因为如果在操作完成之前调用EndXXX,它应该会阻塞直到操作完成。这对于超时并没有太大帮助。

特定的API可能会有所不同(例如,WinForms明确不需要EndInvoke)。

请参见“框架设计准则”(第2版)的§9.2。或msdn


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接