不要在 ASP.NET 4.5 Beta 的 Page 類事件上直接使用 async 與 await
歡迎到我的博客中閱讀獨立版本:http://www.dozer.cc/2012/03/async-and-await-in-asp-net-beta/
發現問題
在我的上一篇文章《async 與 await 在 Web 下的應用》中,我提到了 asp.net 4.5 在 Web.Config 中的一個奇怪配置:
<appSettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> </appSettings>
在 Stack Overflow 上提問后,終于有人回答我了。
看了別人的回復后,才發現了我上篇文章中的問題。
下面代碼中的這種用法是錯誤的:
protected async void Page_Load(object sender, EventArgs e)
{
WebClient client = new WebClient();
var result1 = await client.DownloadStringTaskAsync("http://www.website.com");
WebClient client2 = new WebClient();
var result2 = await client.DownloadStringTaskAsync(result1);
//do more
}
在事件上直接使用 async 引發的錯誤
代碼段一:
public partial class WebForm1 : System.Web.UI.Page
{
protected string Msg { get; set; }
protected async void Page_Load(object sender, EventArgs e)
{
using (WebService service = new WebService())
{
Msg = await service.Method1TaskSync();
}
}
protected async void Button_Test_Click(object sender, EventArgs e)
{
using (WebService service = new WebService())
{
Msg = await service.Method2TaskSync();
}
}
}
試問,最后的 Msg 的值是什么?應該是哪個方法的返回值?
如果去掉異步,那答案肯定是 Method2。那加上異步后呢?
這里用的是 async 和 await 來實現了異步,所以邏輯上的先后次序應該和代碼上的先后次序一樣。
但是上述代碼兩個事件會一起執行!導致了一定的問題!
總結一下上面代碼的問題:當頁面中的 Page_Load 事件和別的事件都用了 async 和 await 后會出現執行次序錯誤、死鎖等問題。它們并不會按次序執行。
代碼段二:
<appSettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> </appSettings>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="AsyncAwait.WebForm1"
Async="true" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<%:Msg %>
</div>
</form>
</body>
</html>
后端代碼和上面一樣的代碼,只不過把 UseTaskFriendlySynchronizationContext 的配置改成了 true,并且把數據顯示到了頁面上。
執行后發現:根本無法顯示內容,頁面在異步執行結束前就已經輸出完畢了。
UseTaskFriendlySynchronizationContext 的作用和錯誤引發的原因
其實在老外的回答中已經說明了全部,我這里主要是翻譯+精簡一下。
UseTaskFriendlySynchronizationContext 的作用:
之前版本的 asp.net 所使用的異步不符合 CLR 的規范,而只有 RegisterAsyncTask 這個方法是符合 CLR 規范的。
所以 asp.net 4.5 中,加入這個新的配置是為了禁用掉之前不符合約定的功能,只要把這個配置設置為了 true,別的異步方案全部會失效。(代碼段二主要就是演示了這個現象)
引發錯誤的原因:
async 和 await 關鍵字在底層主要是利用 SynchronizationContext 來實現了異步。(具體原理我也沒研究過)
而這個方案首先不符合 CLR 規范,另外也會引起很多問題。(代碼段一主要就是演示了其中一個問題)
目前正確的寫法
首先,建議把 UseTaskFriendlySynchronizationContext 設置為 true。
另外,正確的寫法如下:
public partial class WebForm1 : System.Web.UI.Page
{
protected string Msg { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(Method1));
}
private async Task Method1()
{
using (WebService service = new WebService())
{
Msg = await service.HelloWorldTaskSync();
}
}
protected void Button_Test_Click(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(Method2));
}
private async Task Method2()
{
using (WebService service = new WebService())
{
Msg = await service.HelloWorldTaskSync();
}
}
}
如果需要寫異步,一定要用 RegisterAsyncTask 方法,實測證明,支持多次調用,而且會按次序執行。
老外說了,他們也想直接在事件上加 async 來寫,但是由于技術原因并沒有實現,希望在正式版或者未來的版本中可以實現吧!
參考資料:
http://social.msdn.microsoft.com/Forums/en-NZ/async/thread/b2e8c51e-2808-46d0-92e9-b825321d0af8

浙公網安備 33010602011771號