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

4 回答 4

2

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

所以为了完全安全,总是调用 EndInvoke() (这会阻塞,所以在你不需要的一些后台线程上这样做,或者切换到传递回调以免在等待时烧毁线程)。

在实践中,我不知道它的重要性(我认为许多 AsyncResults 不会泄漏,所以在这种情况下你可能会很幸运并且很安全)。

所有这些几乎与请求-响应和 Web 服务器无关,这就是 Begin/End 编程模型的工作原理,无论您在什么应用程序中使用它。

于 2009-02-17T18:11:12.220 回答
1

更新:
似乎您需要始终为异步调用调用 EndInvoke (除非它的 Control.BeginInvoke)或有泄漏资源的风险。

这是一个相同的讨论。建议的解决方案是生成一个线程,等待委托实际完成并调用 EndInvoke。但是,如果发生真正的 Looong 超时,我认为该线程会挂起。

这是一个似乎没有很好记录的边缘案例......也许因为不应该发生超时......例外情况

于 2009-02-17T18:03:47.010 回答
0

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

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

参见“框架设计指南”(第 2 版)第 9.2 节。或msdn

于 2009-02-17T18:44:38.397 回答
0

好吧,我不能忽视我在各处看到的建议,如果我不能确保调用 EndInvoke,我可能会泄漏一些资源,所以我不得不尝试让它在晚上睡觉,而不用担心我正在接近悬崖.

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

为了回答我自己关于 Web 应用程序的问题以及“在我超时并继续前进后有人会在那里听吗”,我发现他们会——即使我超时并继续前进,如果我观看了稍后的输出,那异步call 将返回并运行回调函数,即使我已经将输出返回给客户端。

我使用了我在以下网址找到的一些内容: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.
        }       

    }
}
于 2009-03-11T19:51:16.150 回答