实验——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. IE9+浏览器input文本框/密码框后面的小叉子/小眼睛清除

    为了方便我们的触控操作,IE高等浏览器针对input及input type="password"分别提供了快速清除钮(X图标)以及密码文字显示钮(小眼睛图标)的功能. 由于这经常跟 ...

  2. Java Concurrency - 浅析 Phaser 的用法

    One of the most complex and powerful functionalities offered by the Java concurrency API is the abil ...

  3. JSON-lib框架,JAVA对象与JSON、XML之间的相互转换

    Json-lib可以将Java对象转成json格式的字符串,也可以将Java对象转换成xml格式的文档,同样可以将json字符串转换成Java对象或是将xml字符串转换成Java对象. 一. 准备工作 ...

  4. 输入与enter

    #include<iostream> using namespace std; int main() { char a,b,c; while(scanf("%c%c%c" ...

  5. 最小化Spring XML配置

    Spring提供两种技巧,可以帮助我们减少XML的配置数量. 1.自动装配(autowiring)有助于减少甚至消除配置<property>元素和<constructor-arg&g ...

  6. GSM嗅探

    GSM初探 大家应该都听说过HTTP协议,又听说WEB服务,每一个服务的背后都有一个协议在工作着.所谓的没有规矩不成方圆,说的就是这个道理,每一个细小的部分,都已经规定好,只要按照协议执行,就不会出现 ...

  7. C# Json数据反序列化为Dictionary并根据关键字获取指定值1

    Json数据: { "dataSet": { "header": { "returnCode": "0", " ...

  8. C++ 对数组sizeof 和对数组元素sizeof

    这一段程序 下面这段程序很有看点://arr1 is an array of intsint *source=arr1;size_t sz=sizeof(arr1)/sizeof(*arr1);//n ...

  9. Servlet实现web站点文件下载功能示例

    前段时间事情比较多,导致二月份没有记录自己的学习情况.最近接触了servlet,参考韩老师的教程自己写了一个web站点文件下载的小项目,该项目中还加入了简单的反盗链技术. 1.首先创建一个Shared ...

  10. drop column与set unused

    8i以前,如果需要删除表中的列,需要删除表然后重新建.现在,但我们需要删除一个列时,可以有以下两种方法: Logical Delete Physical Delete Logical Delete(逻 ...