http://www.cnblogs.com/zhaopei/p/async_two.html
那什么是吞吐量呢,也就是本来只能十个人同时访问的网站现在可以二十个人同时访问了。也就是常说的并发量。
们的Web程序天生就是多线程的,且web线程都是跑的线程池线程(使用线程池线程是为了避免不断创建、销毁线程所造成的资源成本浪费),
而线程池线程可使用线程数量是一定的,尽管可以设置,但它还是会在一定范围内。如此一来,我们web线程是珍贵的(物以稀为贵),不能滥用。
用完了,那么其他用户请求的时候就无法处理直接503了。
那什么算是滥用呢?比如:文件读取、URL请求、数据库访问等IO请求。如果用web线程来做这个耗时的IO操作那么就会阻塞web线程,而web线程阻塞得多了web线程池线程就不够用了。也就达到了web程序最大访问数。
此时我们的新异步横空出世,解放了那些原本处理IO请求而阻塞的web线程(想偷懒?没门,干活了。)。
通过异步方式使用相对廉价的线程(非web线程池线程)来处理IO操作,这样web线程池线程就可以解放出来处理更多的请求了。
仔细观察,每个单次请求用时基本上相差不大。 但是步骤7"同步实现"最高投入web线程数是10,而步骤8“异步实现”最高投入web线程数是3。
“异步实现”使用更少的web线程完成了同样的请求数量,如此一来我们就有更多剩余的web线程去处理更多用户发起的请求。
接着我们还发现同步实现请求前后的线程ID是一致的,而异步实现前后线程ID不一定一致。再次证明执行await异步前释放了主线程。
【结论】:
- 使用新异步可以提升Web服务程序的吞吐量
- 对于客户端来说,web服务的异步并不会提高客户端的单次访问速度。
- 执行新异步前会释放web线程,而等待异步执行完成后又回到了web线程上。从而提高web线程的利用率。
=====================================================
Result的死锁陷阱
我们在分析UI单线程程序的时候说过,要慎用异步的Result属性。下面我们来分析:
private void button4_Click(object sender, EventArgs e) { label1.Text = GetUlrString("https://github.com/").Result; } public async Task<string> GetUlrString(string url) { using (HttpClient http = new HttpClient()) { return await http.GetStringAsync(url); } }
代码 GetUlrString("https://github.com/").Result 的Result属性会阻塞(占用)UI线程,而执行到GetUlrString方法的 await异步的时候又要释放UI线程。此时矛盾就来了,由于线程资源的抢占导致死锁。
且Result属性和.Wait()方法一样会阻塞线程。此等问题在Web服务程序里面一样存在。(区别:UI单次线程程序和web服务程序都会释放主线程,不同的是Web服务线程不一定会回到原来的主线程,而UI程序一定会回到原来的UI线程)
我们前面说过,.net为什么会这么智能的自动释放主线程然后等待异步执行完毕后又回到主线程是因为SynchronizationContext的功劳。
====== = == = = = ==
private void button7_Click(object sender, EventArgs e) { label1.Text = GetUrlString("http://www.jb51.net/article/57156.htm").Result; //Result属性会阻塞( 占用)UI线程,而执行GetUrlString方法的异步的时候又要释放UI线程。 //此时就矛盾了,由于线程资源的抢占导致死锁 //且Result属性和.Wait()方法一样会阻塞线程。 //结果是卡死啦 } public async Task < string > GetUrlString(string url) { using(HttpClient http = new HttpClient()) { // return await http.GetStringAsync(url);//会出现死锁 //除了AsyncHelper我们还可以使用Task的ConfigureAwait方法来避免死锁 return await http.GetStringAsync(url).ConfigureAwait(false); //ConfigureAwait(false)会解决死锁 // ConfigureAwait的作用:使当前async方法的await后续操作不需要恢复到主线程(不需要保存线程上下文)。 } }
使用AsyncHelper在同步代码里面调用异步
但可是,可但是,我们必须在同步方法里面执行异步怎办?办法肯定是有的
我们首先定义一个AsyncHelper静态类:
static class AsyncHelper { private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default); public static TResult RunSync<TResult>(Func<Task<TResult>> func) { return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult(); } public static void RunSync(Func<Task> func) { _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult(); } }
然后调用异步:
private void button7_Click(object sender, EventArgs e) { label1.Text = AsyncHelper.RunSync(() => GetUlrString("https://github.com/")); }
这样就不会死锁了。