一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它。也期待大家能够多分享解一些解决实际问题的内容。
在我们遭遇“黑色30秒”问题的过程中,切身体会到了异步的巨大作用(详见从ASP.NET线程角度对“黑色30秒”问题的全新分析),于是开始逐步地用async/await改造现有代码。
今天早上在将一个MVC Controller中的Action改为异步的时候突然发现——其中有7个方法调用可以并行执行。
public async Task<ActionResult> BlogPostInfo(string blogApp, int blogId, int postId, Guid blogUserGuid) { //7个方法无关联的方法调用 }
如果通过async/await实现了这7个方法的并行,性能将会提高几倍,真是一个意外的惊喜!
惊喜之后,则要面对这样一个问题——如何以最低的成本实现?
这7个方法其他地方也在调用,不想直接把这些方法改为异步的;即使可以改为异步的,也不想一路改到底,最后在数据访问层调用ADO.NET的异步方法。
。。。
接着在园子里发现了另外一个惊喜——Jesse Liu的博文(async & await 的前世今生)中的一张图片:
好帅的图!连执行顺序都标得清清楚楚。只要照着这张图,就可以轻松地用async/await实现并行。
需要注意的地方:
1)并行调用的目标方法必须是async的。
2)在并行期间,不能使用await。
以下是实现案例:
下面的代码是需要并行执行的7个方法中的2个:
var tags = TagService.GetTag(blogId, postId); if (!string.IsNullOrEmpty(tags)) { info.Tags = string.Format("标签: {0}", TagService.GetTagLink(blogUrl, tags)); } var categories = CategoryService.GetCateList(blogUrl, blogId, postId); if (!string.IsNullOrEmpty(categories)) { info.Categories = "分类: " + categories; }
由于并行调用的目标方法必须是async的,并且我们不想修改原有的方法实现代码,所以我们增加2个async方法中转一下:
async方法1:
public static async Task<string> GetTagAsync(int blogId, int entryId) { return await Task.Run(() => { return GetTag(blogId, entryId); }); }
async方法2:
public static async string GetCateListAsnyc(string blogUrl, int blogId, int entryId) { return await Task.Run(() => { return GetCateList(blogUrl, blogId, entryId); }); }
然后在调用代码中,分别调用这2个async方法让其并行执行,之后再用await取执行结果。
var tagsTask = TagService.GetTagAsync(blogId, postId); var categoriesTask = BlogCategoryService.GetCateListAsync(blogUrl, blogId, postId); var tags = await tagsTask; if (!string.IsNullOrEmpty(tags)) { info.Tags = string.Format("标签: {0}", TagService.GetTagLink(null, blogUrl, tags)); } var categories = await categoriesTask; if (!string.IsNullOrEmpty((categories))) { info.Categories = "分类: " + categories; }
真的很简单,很轻松! async/await果然好用!
var tagsTask = await TagService.GetTagAsync(blogId, postId);
var categoriesTask = await BlogCategoryService.GetCateListAsync(blogUrl, blogId, postId);
直接等待,两个方法是顺序执行的。
原来还可以这样用(两个业务逻辑不相关的方法)
var tagsTask = TagService.GetTagAsync(blogId, postId);
var categoriesTask = BlogCategoryService.GetCateListAsync(blogUrl, blogId, postId);
两个方法会同时执行,然后再等待
var tags = await tagsTask;
涨知识了~
和下面这样写的区别在哪里?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var
tagsTask = Task.Run(() => GetTag(1, 1));
var
categoriesTask = Task.Run(() => GetCateList(
""
, 1, 1));
var
tags = tagsTask.Result;
if
(!
string
.IsNullOrEmpty(tags))
{
Console.WriteLine(
"标签: {0}"
, tags);
}
var
categories = categoriesTask.Result;
if
(!
string
.IsNullOrEmpty((categories)))
{
Console.WriteLine(
"分类: "
+ categories);
}
|
代码这样写更好看。
http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4
每一个Request进来都会由一个IIS的线程来处理,有时候线程处理完一个请求之后可能不会直接释放,而是立马就去处理其他的请求,这样可以减少创建线程的开销,但是这些线程是有IIS进程池来统一管理和分配的。
进程池里面的线程是有一个最大数量的限制(可以通过IIS来设置),比如说10,那么最多就只能有10个处理线程来处理请求。 这里的async 和await 的优点在于,await 后面有新的线程来处理IO任务,读数据库,文件,或者远程什么的。 (这个新创建的线程不是进程池里面那种用来处理请求的线程),在这种情况下,我们的主线程不会卡在这里,而是会回到线程池中,可以用来处理其他的请求。
所有我们10个处理线程就可以处理超过10个以上的线程。
有人可能会问,await 后面那个任务处理完了怎么办? 如何回到原来的线程?
答案是不用回到原来的线程,线程池会再找一个空闲的线程来处理, 所有有时候,同一个请求会被多个不同的处理线程处理。
每一个Request进来都会由一个IIS的线程来处理,有时候线程处理完一个请求之后可能不会直接释放,而是立马就去处理其他的请求,这样可以减少创建线程的开销,但是这些线程是有IIS进程池来统一管理和分配的。
进程池里面的线程是有一个最大数量的限制(可以通过IIS来设置),比如说10,那么最多就只能有10个处理线程来处理请求。 这里的async 和await 的优点在于,await 后面有新的线程来处理IO任务,读数据库,文件,或者远程什么的。 (这个新创建的线程不是进程池里面那种用来处理请求的线程),在这种情况下,我们的主线程不会卡在这里,而是会回到线程池中,可以用来处理其他的请求...
如果是同一个请求会被不同的线程来处理,那把异步处理的结果响应给客户端是怎么做的?