这种同步的处理方式显然不合理,想要异步处理,只需按如下步骤进行:
1、替换基类Controller为AsyncController
2、创建两个配对的Action:ActionNameAsync和ActionNameCompleted。ActionNameAsync方法必须返回void,在内部启动一个耗时的IO操作前,需要使用AsyncManager.OutstandingOperations.Increment()向MVC框架“注册启动”,在IO方法返回后,可以在AsyncManager.Parameters字典中保存希望传给ActionNameCompleted方法的参数。最后调用AsyncManager.OutstandingOperations.Decrement()通知MVC框架操作完成,此时,MVC框架会自动调用ActionNameCompleted。ActionNameCompleted需要向通常的Action一样,返回一个ActionResult。因此上面的代码需要改写成如下这样:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public void GetPhotoByTagAsync(string tag)
{ //向MVC中注册启动
AsyncManager.OutstandingOperations.Increment();
...
WebRequest request = WebRequest.Create(url);
//启动一个异步的web request
request.BeginGetResponse(asyncResult =>
{
using (WebResponse response = request.EndGetResponse(asyncResult))
{
var xml = XDocument.Load(XmlReader.Create(response.GetResponseStream()));
...
//将结果photoUrls,保存在AsyncManager.Parameters中
AsyncManager.Parameters["photoUrls"] = photoUrls;
//通知MVC框架操作完成 ,准备调用Completed
AsyncManager.OutstandingOperations.Decrement();
}
}, null);
}//像通常的Action一样,这里的参数photoUrls将在AsyncManager.Parameters中匹配public ContentResult GetPhotoByTagCompleted(IEnumerable<string> photoUrls)
{ return Content(string.Format("<img src='{0}'/>", photoUrls.First()));
} |
当然,可以设置异步操作的超时时间:
|
1
2
|
[AsyncTimeout(10000)] // 10000 milliseconds equals 10 seconds
public void GetPhotoByTagAsync(string tag) { ... }
|
上面的代码如果超时了,将抛出TimeoutException异常,我们可以用希望的方式处理它。
当使用类似BeginGetResponse这类的异步方法,并提供回调函数参数时,你无法控制回调函数调用在哪个线程上。大多数情况下,甚至不在ASP.NET的工作线程上。所以回调函数无法关联原始的HttpContext对象。
幸好,AsyncManager提供了一个Sync()方法,它会将一个委托在ASP.NET的工作线程上启动,并关联原始的HttpContext对象。而且它保证线程安全:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
BeginAsyncOperation(asyncResult => { var result = EndAsyncOperation(asyncResult);
// Can't always access System.Web.HttpContext.Current from here...
Action doSomethingWithHttpContext = () => {
// ... but can always access it from this delegate
};if (asyncResult.CompletedSynchronously) // Already on an ASP.NET thread
doSomethingWithHttpContext();
else // Must switch to an ASP.NET thread
AsyncManager.Sync(doSomethingWithHttpContext);
AsyncManager.OutstandingOperations.Decrement();}, null);
|
以上内容只是书中的内容摘录。对异步处理请求,我也没有深入研究,等以后用到了再回过来研究吧。
关于为什么使用异步Controller,这里不做备忘,三岁小孩都懂。主要的备忘是如何使用AsyncController。
1 //这个action以Async结尾,并且返回值是void
2 public void TestAsync()
3 {
4 //实现异步action加计数1
5 AsyncManager.OutstandingOperations.Increment();
6 FileStream fileStream = new FileStream(@"C:\Users\wuxq\Documents\Visual Studio 2010\Projects\MvcAppDemo2\AsyncControllerDemo\并发控制.txt", FileMode.Open);
7 byte[] byteArray = new byte[fileStream.Length];
8 fileStream.BeginRead(byteArray, 0, (int)fileStream.Length, (IAsyncResult result) =>
9 {
10 string content = Encoding.Default.GetString(byteArray);
11 //参数要放在这个字典里面实现向Completed action传递
12 AsyncManager.Parameters["content"] = content;
13 //异步action回调结束
14 AsyncManager.OutstandingOperations.Decrement();
15 fileStream.Close();
16 }, null);
17 }
18
19 //这个action以Completed为后缀异步action结束后调用的函数,返回值为ActionResult
20 public ActionResult TestCompleted(string content)
21 {
22 Response.Write(content);
23 return View();
24 }
此外还要另外注意几点:
1.对于异步请求,当发起另外一个线程去处理请求没有返回怎么办,比如抛出异常?框架默认的超时时间是45秒,在45秒到了之后框架会抛出一个System.TimeoutException以中止这个异步请求,我们可以通过[AsyncTimeOut((int duration)]来设置超时时间,还可以通过NoAsyncTimeout或者[AsyncTimeout(Timeout.Infinite)]来设置永不过期。
2.可以使用AsyncManager.Finish方法来中止所有还未结束的异步操作,进而调用Completed action,如果被强制中止的异步操作还没有成功返回某些参数时,Completed将使用这些参数的默认值(如int为0,string为empty)。
3.AsyncManager.Sync方法的作用
--------------------------------------------------------------------------------------------
可以通过 AsyncController 类编写异步操作方法。 类通常用于长时间运行的 Web 服务调用。
本主题包含以下各节:
Download(下载)网页获得。
这只是一些准则;您必须逐个检查每个应用程序以确定异步操作方法是否能帮助提高性能。
通常,在满足以下条件时使用同步管线:
-
操作很简单或运行时间很短。
-
简单性比效率更重要。
-
对 CPU 绑定操作使用异步操作方法未提供任何好处并且还导致更多的开销。
通常,在满足以下条件时使用异步管线:
-
操作是网络绑定的或 I/O 绑定的而不是 CPU 绑定的。
-
测试显示阻塞操作对于网站性能是一个瓶颈,并且通过对这些阻塞调用使用异步操作方法,IIS 可对更多的请求提供服务。
-
并行性比代码的简单性更重要。
-
您希望提供一种可让用户取消长时间运行的请求的机制。
很少有产品应用程序会显示出如此明显的使用异步操作方法的好处。
Should my database calls be Asynchronous?(我的数据库调用是否应采用异步方式?)。
通常,将少量的同步操作方法转换为异步方法就会显著增加所需的工作量。
显示 Seattle 的新闻。
public class PortalController: Controller { public ActionResult News(string city) { NewsService newsService = new NewsService(); ViewStringModel headlines = newsService.GetHeadlines(city); return View(headlines); } }
操作方法。
public class PortalController : AsyncController { public void NewsAsync(string city) { AsyncManager.OutstandingOperations.Increment(); NewsService newsService = new NewsService(); newsService.GetHeadlinesCompleted += (sender, e) => { AsyncManager.Parameters["headlines"] = e.Value; AsyncManager.OutstandingOperations.Decrement(); }; newsService.GetHeadlinesAsync(city); } public ActionResult NewsCompleted(string[] headlines) { return View("News", new ViewStringModel { NewsHeadlines = headlines }); } }
将同步操作方法转换为异步操作方法包含以下步骤:
-
派生的控制器使 ASP.NET 能够处理异步请求,并且这些控制器仍然可以为同步操作方法提供服务。
-
NewsCompleted。
来引用操作方法。
字典。
-
的调用。
Event-based Asynchronous Pattern Overview。
方法来完成整个异步操作。
请注意下面有关异步操作方法的一些事项:
-
方法。
-
SampleAsync。)
-
操作方法具有相同的请求签名。
例如,门户网站可能不只显示新闻,还显示体育、天气、股票和其他信息。
操作方法的同步版本。
public ActionResult IndexSynchronous( string city ) { NewsService newsService = new NewsService(); string[] headlines = newsService.GetHeadlines(); SportsService sportsService = new SportsService(); string[] scores = sportsService.GetScores(); WeatherService weatherService = new WeatherService(); string[] forecast = weatherService.GetForecast(); return View("Common", new PortalViewModel { NewsHeadlines = headlines, SportsScores = scores, Weather = forecast }); }
但是,如果异步执行服务调用(以并行方式),则总的响应时间将稍微大于 600 毫秒,因为这是最长任务的持续时间。
操作方法的异步版本。
public void IndexAsync(string city) { AsyncManager.OutstandingOperations.Increment(3); NewsService newsService = new NewsService(); newsService.GetHeadlinesCompleted += (sender, e) => { AsyncManager.Parameters["headlines"] = e.Value; AsyncManager.OutstandingOperations.Decrement(); }; newsService.GetHeadlinesAsync(); SportsService sportsService = new SportsService(); sportsService.GetScoresCompleted += (sender, e) => { AsyncManager.Parameters["scores"] = e.Value; AsyncManager.OutstandingOperations.Decrement(); }; sportsService.GetScoresAsync(); WeatherService weatherService = new WeatherService(); weatherService.GetForecastCompleted += (sender, e) => { AsyncManager.Parameters["forecast"] = e.Value; AsyncManager.OutstandingOperations.Decrement(); }; weatherService.GetForecastAsync(); } public ActionResult IndexCompleted(string[] headlines, string[] scores, string[] forecast) { return View("Common", new PortalViewModel { NewsHeadlines = headlines, SportsScores = scores, Weather = forecast }); } }
方法,这是因为有三个异步操作。
关于为什么使用异步Controller,这里不做备忘,三岁小孩都懂。主要的备忘是如何使用AsyncController。
1 //这个action以Async结尾,并且返回值是void
2 public void TestAsync()
3 {
4 //实现异步action加计数1
5 AsyncManager.OutstandingOperations.Increment();
6 FileStream fileStream = new FileStream(@"C:\Users\wuxq\Documents\Visual Studio 2010\Projects\MvcAppDemo2\AsyncControllerDemo\并发控制.txt", FileMode.Open);
7 byte[] byteArray = new byte[fileStream.Length];
8 fileStream.BeginRead(byteArray, 0, (int)fileStream.Length, (IAsyncResult result) =>
9 {
10 string content = Encoding.Default.GetString(byteArray);
11 //参数要放在这个字典里面实现向Completed action传递
12 AsyncManager.Parameters["content"] = content;
13 //异步action回调结束
14 AsyncManager.OutstandingOperations.Decrement();
15 fileStream.Close();
16 }, null);
17 }
18
19 //这个action以Completed为后缀异步action结束后调用的函数,返回值为ActionResult
20 public ActionResult TestCompleted(string content)
21 {
22 Response.Write(content);
23 return View();
24 }
此外还要另外注意几点:
1.对于异步请求,当发起另外一个线程去处理请求没有返回怎么办,比如抛出异常?框架默认的超时时间是45秒,在45秒到了之后框架会抛出一个System.TimeoutException以中止这个异步请求,我们可以通过[AsyncTimeOut((int duration)]来设置超时时间,还可以通过NoAsyncTimeout或者[AsyncTimeout(Timeout.Infinite)]来设置永不过期。
2.可以使用AsyncManager.Finish方法来中止所有还未结束的异步操作,进而调用Completed action,如果被强制中止的异步操作还没有成功返回某些参数时,Completed将使用这些参数的默认值(如int为0,string为empty)。
3.AsyncManager.Sync方法的作用