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可能會在某些情況下引發異常。理論上,這可以很容易地複製。