实验——async什么时候提高吞吐

async是一个语法糖,用来简化异步编程,主要是让异步编程在书写上接近于同步编程。总的来收,在await的时候,相当于附加上了一个.ContinueWith()。

至于为什么async能够提高吞吐,是因为通过async方法返回一个Task对象,IIS缩减了工作线程的处理时间长短(切换到了其他线程,且没有阻塞当前线程),从而提高了单位时间的处理量。这里还有其他的一些细节,详情见这篇博文:

http://www.cnblogs.com/rosanshao/p/3728108.html

关于async的使用,参考这篇博文:

http://www.asp.net/mvc/overview/performance/using-asynchronous-methods-in-aspnet-mvc-4

博主曾经花了一个下午点时间测试性能,就是没有获得期望的结果,用的就是此博文中举出的反例。当时博主心想“既然TPL中的Task+async就能提高性能,那么为什么EF还要特地的提供XXXAsync方法?这不是让别人更加困惑么?”所以博主就打算不用数据库,简单撸一个Task测一测,看看是不是和我想象中的一般逆天。

根据这篇博客的描述,IIS的线程分为工作线程和IO线程两种,其中工作线程总数被限制在一个阈值,所以减少工作线程的利用效率可以提高吞吐。而在asp.net中,切换线程就分为两种:工作线程->IO线程,工作线程->工作线程(反例)。假定一个工作线程每使用async之前每请求工作1秒,通过切换,IO线程工作的时候,他去处理其他请求,把平均工作时间降为了0.5秒,这样吞吐理想情况下就翻倍了。但是...如果是工作线程->工作线程,虽然对于单个线程而言是减少了,但是其他工作线程又会扔活过来,总体来说没有变化,反而因为交接的问题,性能有所下降...

先用几个负载测试来支持以上言论

首先定义一个提供各种操作的辅助类。

public class BaseFairHelper
{
public Task<string> SayHelloTask()
{
return Task<string>.Factory.StartNew(() =>
{
Thread.Sleep();
return "Hello";
});
} public async Task<string> SayHelloAsync()
{
return await Task<string>.Factory.StartNew(() =>
{
Thread.Sleep();
return "Hello";
});
} public string SayHello()
{
Thread.Sleep();
return "Hello";
}
}

BaseFairHelper

1.基础测试

假定我们任意启动一个Task就可以达到解放IIS工作线程的目的,那么,对于两个Action,一个执行工作量1的同步操作,一个执行工作量1的同步操作外带一个工作量1的一步操作,这两个Action在吞吐以及性能表现上应该相差无几。代码如下:

/// <summary>
/// 异步
/// </summary>
/// <returns></returns>
public ActionResult BaseAsync()
{
var helper = new BaseFairHelper();
var task1 = helper.SayHelloTask();
var str = helper.SayHello();
task1.Wait();
return Content(str);
} /// <summary>
/// 对照
/// </summary>
/// <returns></returns>
public ActionResult BaseAsync_()
{
var helper = new BaseFairHelper();
var task1 = helper.SayHelloTask();
var task2 = helper.SayHelloTask();
var str = helper.SayHello();
Task.WaitAll(task1, task2);
return Content(str);
} /// <summary>
/// 基础对照
/// </summary>
/// <returns></returns>
public ActionResult Base()
{
var helper = new BaseFairHelper();
return Content(helper.SayHello());
}

Base Test

然后使用VS的负载测试,测试模式选为增量,结果如下:

工作量 情况 吞吐量(min) 吞吐量(max) 时长per请求(max) 时长per请求(min) 吞吐均值 时长均值
(base)1 同步 8 200 1.02 1.01 145 1.02
(baseasync)2 同步+异步 8 120 1.76 1.01 98.7 1.53
(baseasync_)3 同步+异步x2 0 89.4 2.59 1.01 69.5 2.1
               

可以发现性能相差明显,但是在低并发情况下,性能表现是我们预期的,高并发的时候,则不然。最大吞吐也不是我们预期的。这点上可以支持“IIS工作线程”有限的观点。

2.Fair测试

以上,这是一组对比测试,工作量并不同,现在进行一组工作量相同的测试。其中一个Action执行同步x2的操作,另一个执行同步+异步组合的操作。代码如下:

public ActionResult FairAsync()
{
var helper = new BaseFairHelper();
var task = helper.SayHelloTask();
var str = helper.SayHello();
task.Wait();
return Content(str);
} public ActionResult Fair()
{
var helper = new BaseFairHelper();
var str = helper.SayHello();
str = helper.SayHello();
return Content(str);
}

Fair Test

同样适用负载测试,测试模式选为高并发(200用户数):

工作量 情况 吞吐量(min) 吞吐量(max) 时长per请求(max) 时长per请求(min) 吞吐均值 时长均值
(fair)2 同步 8 112 2.03 2 85 2.01
(fairasync)2 同步+异步 20 125 1.85 1 107 1.59

和低并发(25用户数):

工作量 情况 吞吐量(min) 吞吐量(max) 时长per请求(max) 时长per请求(min) 吞吐均值 时长均值
(fair)2 同步 1 12.6 2.03 2 10.7 2.02
(fairasync)2 同步+异步 2 25 1.02 1 21.3 1.01

可以看到,由于工作线程争用,导致使用Task的异步方案在高并发的情况下,单个请求的性能有所下降(时长从1->1.85),这也从侧面证明了以上的观点。

async方法提供吞吐的情况

这里是我参考的文章:【http://www.dotnetcurry.com/aspnet-mvc/948/webapi-async-performance-aspnet-mvc-application

以及这篇文章附带的代码:【http://pan.baidu.com/s/1ntxNX4t

博主针对数据库(EF)的async做了很多次实验,结果发现同步和异步在吞吐以及性能表现上几乎一致(参考文章末尾附件中的测试结果截图)。于是最终返回这篇文章,并针对这篇文章中的代码进行测试,同时结合自己的思考重新编写了测试——结果仍然没有感受到duang一下的特效。所以暂时不纠结了。

【此处应该有跟进和更新】

使用async的几个姿势

对于以下两个异步方法:

public class AsyncMethods
{
public static async Task<string> Async1()
{
return await Task<string>.Factory.StartNew((t) =>
{
Task.Delay().Wait();
return "hello";
}, null);
} public static async Task<string> Async2()
{
return await Task<string>.Factory.StartNew(t =>
{
Thread.Sleep();
return "hello";
}, null);
}
}

async methods

1.对多个async方法进行同步等待

[ActionName("IndexAsync2")]
public async Task<ActionResult> IndexAsync2()
{
var task1 = AsyncMethods.Async1();
var task2 = AsyncMethods.Async2();
await Task.WhenAll(task1, task2);
return Content(task2.Result);
}

多任务等待

2.有序执行多个async

[ActionName("IndexAsync1")]
public async Task<ActionResult> IndexAsync1()
{
string result = await AsyncMethods.Async1();
result = result + await AsyncMethods.Async2();
return Content(result);
}

有序等待

3.死锁(反例)

简单将await方法迁移到同步方法中,都会导致线程死锁(ASP.NET环境下)。由于异步方法执行完成后的操作要求回到调用的上下文(线程),会等待调用上下文。而Wait()方法表示等待异步方法完成。所以你等我我等你,死锁。

public ActionResult Index1()
{
AsyncMethods.Async1().Wait();
return Content("");
} public ActionResult Index2()
{
var task1 = AsyncMethods.Async1();
var task2 = AsyncMethods.Async2();
Task.WhenAll(task1, task2);
return Content(task1.Result);
}

dead lock

为何async能够防止ASP.NET工作线程等待

参考这篇文章:【http://blog.stevensanderson.com/2008/04/05/improve-scalability-in-aspnet-mvc-using-asynchronous-requests/】的图。

async提高性能的情况

这是并行编程的情况,总的来说就是充分利用CPU,个人认为这更多的是Task的功劳。async这个关键字更多的像是将一些列的ContinueWith连锁在同一个线程(上下文)之上,防止线程切换。

在CQRS中实现Command的异步执行

经过多日的实验和纠结(惭愧),对async的看法有了点转变。async关键字现在给我的感觉,更像是从“骨子里”的异步,因为调用async方法的时候,要求调用方也指明async(或者你可以开一个Task去执行...然而...太蠢)。这感觉是让C#的中的所有方法(指明async)天生就是异步架构的(无端想起了F#)。所以,为Cqrs添加异步功能就分为两块:

1.为CommandBus添加一个SendAsync的方法

2.实现一个完全基于异步的Cqrs【想法,想法,只是想法...】【此处应有后续跟进】

先撸第一个:

 public interface ICommandBus
{
void Send<T>(T command) where T : ICommand; Task SendAsync<T>(T command) where T : ICommand;
} void ICommandBus.Send<T>(T command)
{
var handler = CommandHandlerSearcher.Find<T>(); #region auditing var auditInfo = CommandEventAuditInfo.StartNewForCommand<T>(handler.GetType());
auditInfo.Start(); #endregion handler.Execute(command); #region audting auditInfo.Stop(); #endregion Test.Configuration.AuditStorage.Save(auditInfo);
} public ICommandHandlerSearcher CommandHandlerSearcher { get; set; } Task ICommandBus.SendAsync<T>(T command)
{
ICommandBus bus = this;
return Task.Factory.StartNew(() => bus.Send(command));
}

command bus

然后是测试结果:

同时,在修改Auditing支持异步的同时,发现了自己以前实现的Auditing有问题

至于为什么不考虑实现EventBus支持异步...那是因为,博主当前的工作单元是基于线程的(简单粗暴的将一个Command视为原子操作)。

与async有关的代码:【http://pan.baidu.com/s/1sjA7gbN

此篇完成时,所使用的代码:【http://pan.baidu.com/s/1sjsqiZV

CQRS学习——Cqrs补丁,async实验以及实现[其二]的更多相关文章

  1. CQRS学习——IOC,配置,仓储隔离以及QueryEntry[其三]

    从IoC开始说起 博主最早开始用的IoC容器叫AutoFac,那时候用它主要是为了生命周期管理——将EF上下文的生命周期限定为每请求.当然也总是每每听到IoC的好处,但是仍然不能理解其优势.最近在学习 ...

  2. 【StatLearn】统计学习中knn算法实验(2)

    接着统计学习中knn算法实验(1)的内容 Problem: Explore the data before classification using summary statistics or vis ...

  3. CQRS学习——最小单元的Cqrs(CommandEvent)[其一]

    [说明:博主采用边写边思考的方式完成这一系列的博客,所以代码以附件为准,文中代码仅为了说明.] 结构 在学习和实现CQRS的过程中,首要参考的项目是这个[http://www.cnblogs.com/ ...

  4. CQRS学习——Dpfb以及其他[引]

    [Dpfb的起名源自:Ddd Project For Beginer,这个Beginer自然就是博主我自己了.请大家在知晓这是一个入门项目的事实上,怀着对入门者表示理解的心情阅读本系列.不胜感激.] ...

  5. CQRS学习——集成ASP.NET Identity[其五]

    [其实和Cqrs没啥关系] 缘由 其实没啥原因,只是觉得以前写了不知多少遍的用户登录复用性太差,实现的功能也不多. 依赖的Nuget包 简单登陆 就简单登陆而言,只需要实现如下接口/抽象类: Stor ...

  6. CQRS学习——Storage实现(EF+Code First+DynamicReponsitory)[其四]

    [这里是的实现,指的是针对各个数据访问框架的一个基础实现] 目标 定义仓储/QueryEntry的基本功能 实现仓储的基本功能,以利于复用 实现一些常用的功能 提供一些便利的功能 目标框架 博主使用的 ...

  7. CQRS学习——一个例子(其六)

    [先上链接:http://pan.baidu.com/s/1o62AHbc ] 多图杀猫 先用一组图看看实现的功能: 添加一个功能 假定现在要添加一个书本录入的功能,那么执行如下的操作: 1.添加Co ...

  8. .NET异步操作学习之一:Async/Await中异常的处理

    以前的异常处理,习惯了过程式的把出现的异常全部捕捉一遍,然后再进行处理.Async/Await关键字出来之后的确简化了异步编程,但也带来了一些问题.接下来自己将对这对关键字进行学习.然后把研究结果放在 ...

  9. Arduino学习笔记⑤ 模拟IO实验

    1.前言     还记得前几个我们都是在讲解数字IO,而其实我们生活中大多数信号都是模拟信号,如声音以及温度变化.在Arduino中,常用0~5v的电压来表示模拟信号. 1.1 模拟输入功能      ...

随机推荐

  1. PHP之自定义会话控制---使用文件处理

    前三篇简单的总结了下会话控制和文件操作,这一篇说说会话控制的自定义处理方式.既然知道了文件的基本读写,而且在会话控制中,也有人提到,session数据可以保存到缓存或数据库中,实际上当然不会是直接利用 ...

  2. JS轮播图

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  3. Jersey(1.19.1) - Rules of Injection

    Previous sections have presented examples of annotated types, mostly annotated method parameters but ...

  4. Unity3D 之UGUI 滚动条

    先上效果图. 这里来说明下UGUI 滚动条,不涉及到代码. 主要用到的控件Scroll Rect ,Mask,Scrollbar. 第一步,建立一个Image,然后绑定一个滑动块的组件,添加一个mas ...

  5. replace替换语句

    t_sql语句:replace替换语句:update 表名 set 列名=REPLACE(列名,'替换的数据','替换后的数据')

  6. iOS 在viewController中监听Home键触发以及重新进入界面的方法

    第一步:创建2个NSNotificationCenter监听 [[NSNotificationCenter defaultCenter] addObserver:self selector:@sele ...

  7. using System.Threading;

    /// <summary> /// 执行动作:耗时而已 /// </summary> private void TestThread(string threadName) { ...

  8. 抛弃 CSS Hacks 后的浏览器兼容方案

    一般情况下的浏览器兼容需要考虑 IE6/7/8 三种 IE 版本,当然在 IE9 开始逐步推向市场后,又会有更多的衍生版本.所以我目前只考虑 IE7~9 版本的兼容情况.涉及到的条件注释代码如下: & ...

  9. 详解null

    前言 在java中初始化的时候经常用到null,也经常会碰到空指针异常(NullPointerException),由于碰到的频率比较高,我认为有必要进行一下了解,揭开它的神秘面纱. 一.null是代 ...

  10. Android Studio生成APK自动追加版本号

    转载说明 本篇文章可能已经更新,最新文章请转:http://www.sollyu.com/android-apk-studio-generated-automatically-appends-a-ve ...