【xiaotie】Asp.Net異常Asynchronous 的解決方案
方法A()的執行過程如下:
A()呼叫第三方庫的非同步方法B(),在非同步方法的回撥方法C()之中,又呼叫了非同步WebService Request。
經過除錯發現,在C()中,吞噬了系統丟擲的異常。修改程式碼,捕獲到一個異常:
Asynchronous operations are not allowed in this context. Page starting an asynchronous operation has to have the Async attribute set to true and an asynchronous operation can only be started on a page prior to PreRenderComplete event.
搜尋 Google 發現,asp.net 對非同步呼叫進行了限制,如要在 Page 中進行非同步操作,需要設定Page的 Async = true; 然而,問題是在Page中觸發的僅僅是方法A,A再呼叫方法B,B的回撥則是由無名的苦工執行緒在幕後執行的,C的回撥又是一個無名的苦工執行緒在幕後執行的,兩個苦工執行緒,和Page執行緒根本不是一個執行緒,因此,在Page中設定Async=true能否影響到後臺苦工執行緒值得懷疑。
在Page中加入 Async = true,果然,還是不能得出正確結果。無奈之下,猜測能否在配置檔案中設定一項,啟用全站的非同步操作。搜尋設定項,無果,只好尋求第三條道路。
仔細分析異常源,是由一個System.Web.AspNetSynchronizationContext例項的OperationStarted()方法丟擲的,在MSDN中沒搜到這個類。動用.Net Reflector,原來是一個internal類,OperationStarted()方法如下:
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> public override void OperationStarted()
{
if (this._invalidOperationEncountered || (this._disabled && (this._pendingCount == 0)))
{
this._invalidOperationEncountered = true;
throw new InvalidOperationException(SR.GetString("Async_operation_disabled"));
}
Interlocked.Increment(ref this._pendingCount);
}
持有 AspNetSynchronizationContext 的是一個靜態類 System.ComponentModel.AsyncOperationManager的靜態屬性SynchronizationContext。
用reflecter檢視程式碼, SynchronizationContext的getter獲取的是當前執行緒的SynchronizationContext,它的setter存取的是當前執行緒的SynchronizationContext。也就是說,對於丟擲異常的執行緒,它的SynchronizationContext是一個AspNetSynchronizationContext例項。
編寫測試程式發現,控制檯程式的SynchronizationContext使用的SynchronizationContext型別是 System.Threading.SynchronizationContext,Asp.Net程式使用的SynchronizationContext型別是System.Web.AspNetSynchronizationContext。
.Net 程式中,WebService使用SoapHttpClientProtocol進行Soap Request,而SoapHttpClientProtocol是WebClient類的子類,在WebClient類的許多非同步方法中,都呼叫了System.ComponentModel.AsyncOperationManage.SynchronizationContext.OperationStarted()方法. 如果碰上當前執行緒應用的是AspNetSynchronizationContext,而該AspNetSynchronizationContext又不允許非同步操作,就出現了異常。
瞭解到這些,解決方案呼之欲出,就是在 global.asax 裡 加上 System.ComponentModel.AsyncOperationManager.SynchronizationContext=new System.Threading.SynchronizationContext();
測試一下,啊哈~~~失敗!!
Page中列印出來的SynchronizationContext還是AspNetSynchronizationContext。
用 Reflector 查詢 AsyncOperationManager 的被引用情況,發現方法 System.Web.HttpApplication+ThreadContext.Enter(Boolean) : Void。方法體:
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->internal void Enter(bool setImpersonationContext)
{
this._savedContext = HttpContextWrapper.SwitchContext(this._context);
if (setImpersonationContext)
{
this.SetImpersonationContext();
}
this._savedSynchronizationContext = AsyncOperationManager.SynchronizationContext;
AsyncOperationManager.SynchronizationContext = this._context.SyncContext;
Guid requestTraceIdentifier = this._context.WorkerRequest.RequestTraceIdentifier;
if (!(requestTraceIdentifier == Guid.Empty))
{
CallContext.LogicalSetData("E2ETrace.ActivityID", requestTraceIdentifier);
}
this._context.ResetSqlDependencyCookie();
this._savedPrincipal = Thread.CurrentPrincipal;
Thread.CurrentPrincipal = this._context.User;
this.SetRequestLevelCulture(this._context);
if (this._context.CurrentThread == null)
{
this._setThread = true;
this._context.CurrentThread = Thread.CurrentThread;
}
}
原來SynchronizationContext在這裡被偷樑換柱了。吃飽了撐的。
你不仁,我不義。你將它換過去,我就將它換過來。在頁面的Page_Load()裡面加上:
System.ComponentModel.AsyncOperationManager.SynchronizationContext=new System.Threading.SynchronizationContext();
測試通過。
不過這樣做副作用還不小,比如SynchronizationContext經常會被HttpApplication替換成AspNetSynchronizationContext,在這時進行非同步操作就會出問題,再比如,使用System.Threading.SynchronizationContext而不是AspNetSynchronizationContext,會導致Asp.Net自身出現異常,昨天執行時就有一次HttpApplication拋了個異常。
為減少副作用,那就在呼叫方法A()之前替換一下SynchronizationContext,方法執行完畢,再替換過來:
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> System.Threading.SynchronizationContext context = System.ComponentModel.AsyncOperationManager.SynchronizationContext;
try
{
System.ComponentModel.AsyncOperationManager.SynchronizationContext = new System.Threading.SynchronizationContext();
A();
}
finally
{
System.ComponentModel.AsyncOperationManager.SynchronizationContext = context;
}
測試通過。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-410180/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- [持續更新]hive異常解決方案Hive
- Redis快取的主要異常及解決方案Redis快取
- native程式異常crash定位解決方案
- 77種Java異常總結 附解決方案Java
- SpringCloud BeanCurrentlyInCreationException 異常解決方案SpringGCCloudBeanException
- 一種 Laravel 異常上下文解決方案Laravel
- maven異常:Updating Maven Project 的統一解決方案MavenProject
- Tomcat常見異常及解決方案程式碼例項Tomcat
- javaWeb常見異常的解決方法JavaWeb
- Ionic異常及解決
- asp.net iis7預設文件錯誤異常的解決方法ASP.NET
- migrate:rollback 時 dropForeign 丟擲的異常解決方案
- Oracle Net Configuration Assistant failed異常的解決方案OracleAI
- flume file channel 異常解決
- Unknowncolumn'*'in'fieldlist'異常解決
- Mysql 的一些異常解決MySql
- JVM 異常退出的問題解決JVM
- CentOS 常見異常及解決辦法CentOS
- CrashSight異常崩潰管理解決方案
- “The last packet sent successfully to the server was 0 milliseconds ago. ”異常解決方案ASTServerGo
- Shiro身份驗證丟擲AuthenticationException異常,解決方案Exception
- 關於SpringMVC的HttpMediaTypeNotSupportedException異常解決SpringMVCHTTPException
- ASP.NET異常處理ASP.NET
- 高併發解決方案詳解(9大常見解決方案)
- 常見的跨域解決方案(全)跨域
- git 常見問題的解決方案Git
- 常見的高可用MySQL解決方案MySql
- 常見的高可用 MySQL 解決方案MySql
- Java解析xml檔案遇到特殊符號&會出現異常的解決方案JavaXML符號
- 2015異常問題解決方案經驗總結(一)
- asp.net Ajax 終極解決方案ASP.NET
- asp.net 連結 oracle 解決方案ASP.NETOracle
- No bean named 'cacheManager' availablej 異常解決BeanAI
- 資料庫異常hang住解決資料庫
- ASP.NET MVC 異常處理ASP.NETMVC
- Spring10種常見異常解決方法Spring
- Spring Data JPA 報 HOUR_OF_DAY: 0 -> 1異常的解決過程和方案Spring
- 阿里雲異常流量及異常網路連線的安全解決過程阿里