【问题标题】:Do I need to call EndInvoke after a timeout?超时后是否需要调用 EndInvoke?
【发布时间】:2009-02-17 17:41:30
【问题描述】:

在网页上,我打电话给不允许我以编程方式设置超时的第三方。我调用 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;
    }
}

【问题讨论】:

    标签: timeout begininvoke


    【解决方案1】:

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

    为了完全安全,请始终调用 EndInvoke() (这会阻塞,所以在您不需要的一些后台线程上执行此操作,或者切换到传递回调以免在等待时烧毁线程)。

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

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

    【讨论】:

    • 关于请求-响应:如果我传递了一个回调方法,但同时完成了我的 buttonclick 函数并返回到客户端,那么当 BeginInvoke 线程返回时,是否有人在监听?在本地应用中,如果 Main() 完成并关闭应用,回调永远不会被触发。
    【解决方案2】:

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

    这里是a discussion that is on the same lines。建议的解决方案是生成一个线程,该线程将等待委托实际完成并调用 EndInvoke。但是,如果发生真正的 Looong 超时,我认为该线程会挂起。

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        好吧,我不能忽视我在各处看到的建议,如果我没有确保调用 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.
                }       
        
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-09
          • 1970-01-01
          • 1970-01-01
          • 2016-11-09
          • 1970-01-01
          • 2014-09-17
          • 1970-01-01
          • 2011-01-23
          相关资源
          最近更新 更多