0

我的应用程序每隔几秒查询我的服务器以进行更新。在HttpWebRequest.Abort下请求取消的异常

离开它运行了大约3天后,我观察到应用程序崩溃了以下堆栈跟踪。

正如您所知,在工作线程中获取异常时,无法捕获该异常,因此我的应用程序崩溃了。

System.Net.WebException: The request was canceled 
System.Net.ServicePointManager.FindServicePoint(Uri address, IWebProxy proxy, ProxyChain& chain, HttpAbortDelegate& abortDelegate, Int32& abortState) 
System.Net.HttpWebRequest.FindServicePoint(Boolean forceFind) 
System.Net.AuthenticationState.PrepareState(HttpWebRequest httpWebRequest) 
System.Net.AuthenticationState.ClearSession(HttpWebRequest httpWebRequest) 
System.Net.HttpWebRequest.ClearAuthenticatedConnectionResources() 
System.Net.HttpWebRequest.Abort(Exception exception, Int32 abortState) 
System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 
System.Threading.ThreadPoolWorkQueue.Dispatch() 

我在网上看到了许多类似的线程。但所有拥有同样筹码的人都没有得到任何帮助。

我也看到很多人建议设置我的HttpWebRequest的属性KeepAlive=false,但是,这可能会伤害我的表现,并且是不可接受的。

回答

0

这是,事实上,在微软的框架,一个已知的bug,广告这里所说:

https://support.microsoft.com/en-us/kb/2750147

关于这个奇怪的问题是,我的应用程序与.NET4.0,而不是.NET4运行.5

在与Microsoft的支持部门交谈之后,似乎如果.NET4.5在机器上安装了,则将更改应用程序的行为。所以它实际上是有道理的。

证据可以在MS的源代码中找到。从http://referencesource.microsoft.com/#q=httpwebrequest

 // TimeoutCallback - Called by the TimerThread to abort a request. This just posts ThreadPool work item - Abort() does too 
     // much to be done on the timer thread (timer thread should never block or call user code). 
     private static void TimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context) 
     { 
      ThreadPool.UnsafeQueueUserWorkItem(s_AbortWrapper, context); 
     } 

     private void Abort(Exception exception, int abortState) 
     { 
      GlobalLog.ThreadContract(ThreadKinds.Unknown, "HttpWebRequest#" + ValidationHelper.HashString(this) + "::Abort()"); 
      if (Logging.On) Logging.Enter(Logging.Web, this, "Abort", (exception == null? "" : exception.Message)); 

      if(Interlocked.CompareExchange(ref m_Aborted, abortState, 0) == 0) // public abort will never drain streams 
      { 
       GlobalLog.Print("HttpWebRequest#" + ValidationHelper.HashString(this) + "::Abort() - " + exception); 

       NetworkingPerfCounters.Instance.Increment(NetworkingPerfCounterName.HttpWebRequestAborted); 

       m_OnceFailed = true; 
       CancelTimer(); 

       WebException webException = exception as WebException; 
       if (exception == null) 
       { 
        webException = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled); 
       } 
       else if (webException == null) 
       { 
        webException = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), exception, WebExceptionStatus.RequestCanceled, _HttpResponse); 
       } 

       try 
       { 
#if DEBUG 
        bool setResponseCalled = false; 
        try 
        { 
#endif 
         // Want to make sure that other threads see that we're aborted before they set an abort delegate, or that we see 
         // the delegate if they might have missed that we're aborted. 
         Thread.MemoryBarrier(); 
         HttpAbortDelegate abortDelegate = _AbortDelegate; 
#if DEBUG 
         m_AbortDelegateUsed = abortDelegate == null ? (object)DBNull.Value : abortDelegate; 
#endif 
         if (abortDelegate == null || abortDelegate(this, webException)) 
         { 
          // We don't have a connection associated with this request 

#if DEBUG 
          setResponseCalled = true; 
#endif 
          SetResponse(webException); 
         } 
         else 
         { 
          // In case we don't call SetResponse(), make sure to complete the lazy async result 
          // objects. abortDelegate() may not end up in a code path that would complete these 
          // objects. 
          LazyAsyncResult writeAResult = null; 
          LazyAsyncResult readAResult = null; 

          if (!Async) 
          { 
           lock (this) 
           { 
            writeAResult = _WriteAResult; 
            readAResult = _ReadAResult; 
           } 
          } 

          if (writeAResult != null) 
           writeAResult.InvokeCallback(webException); 

          if (readAResult != null) 
           readAResult.InvokeCallback(webException); 
         } 

         if (!Async) 
         { 
          LazyAsyncResult chkConnectionAsyncResult = ConnectionAsyncResult; 
          LazyAsyncResult chkReaderAsyncResult = ConnectionReaderAsyncResult; 

          if (chkConnectionAsyncResult != null) 
           chkConnectionAsyncResult.InvokeCallback(webException); 
          if (chkReaderAsyncResult != null) 
           chkReaderAsyncResult.InvokeCallback(webException); 
         } 

         if (this.IsWebSocketRequest && this.ServicePoint != null) 
         { 
          this.ServicePoint.CloseConnectionGroup(this.ConnectionGroupName); 
         } 
#if DEBUG 
        } 
        catch (Exception stressException) 
        { 
         t_LastStressException = stressException; 
        if (!NclUtilities.IsFatal(stressException)){ 
         GlobalLog.Assert(setResponseCalled, "HttpWebRequest#{0}::Abort|{1}", ValidationHelper.HashString(this), stressException.Message); 
         } 
         throw; 
        } 
#endif 
       } 
       catch (InternalException) 
       { 
       } 
      } 

      if(Logging.On)Logging.Exit(Logging.Web, this, "Abort", ""); 
     } 

正如你所看到的,TimeoutCallback正在调用一个新的线程中止方法,这意味着它不是异常的证明。

此外,Abort可能会在某些情况下引发异常。理论上,这可以很容易地复制。