实验——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. ansible安装(批量执行命令

    rpm安装 下载epl源 :  Download the latest epel-release rpm from:http://dl.fedoraproject.org/pub/epel/6/x86 ...

  2. 如何找出MySQL数据库中的低效SQL语句

    面对业务的迅猛发展,DBA的一项重要工作就是及时发现数据库中的低效SQL语句,有的可以立刻着手解决(比如缺少合适的索引),有的需要尽快反馈给开发人员进行修改. MySQL数据库有几个配置选项可以帮助我 ...

  3. PHP学习笔记 - 进阶篇(10)

    PHP学习笔记 - 进阶篇(10) 异常处理 抛出一个异常 从PHP5开始,PHP支持异常处理,异常处理是面向对象一个重要特性,PHP代码中的异常通过throw抛出,异常抛出之后,后面的代码将不会再被 ...

  4. 读<<CLR via C#>> 详谈泛型

    1,什么是泛型? 答:泛型是类型的模板,类型是实例(对象)的模板.C#提供了5种泛型:类,接口,委托,结构和方法. 2,使用泛型有什么好处? 答:继承实现的是"代码重用",而泛型实 ...

  5. 动态链接库加载出错:cannot restore segment prot after reloc: Permission denied

    在执行可执行程序时,出现动态链接库加载出错:cannot restore segment prot after reloc: Permission denied. 主要是由于Linux 内核中提供的强 ...

  6. CCM加密学习

    这几天终于搞定了AES硬件加密工具的使用,几种简单的加密模式也都实验通过了,比较麻烦的一种是CCM模式的加密,它是CTR加密模式和CMAC认证算法的混合使用.本文先介绍CCM模式的原理与基本实现,然后 ...

  7. Contiki学习入门之概览

    Contiki是专为物联网领域而设计的开源操作系统,适用于联网嵌入式系统和无线传感器网络.由瑞典计算机科学学院的Adam Dunkels团队开发.它有以下几个特点. 1. 网络标准 contiki提供 ...

  8. 视酷即时通讯系统应用源码 V1.0

    视酷即时通讯系统(原创),成熟稳定,拥有和微信一样强大的功能不再是梦,节省几个月研发时间迅速融合进项目中: 1.首家支持聊天室群聊 2.支持和微信一样的语音聊天,可以显示时长.未读状态,自动轮播未读语 ...

  9. 在Windows下设置环境变量 运行mysql程序变得更容易

    在Windows下设置环境变量,点开始菜单,右键单击我的电脑--属性--高级--环境变量 可以看到PATH的变量是这样的: C:\WINDOWS;C:\WINDOWS\COMMAND   为了让运行m ...

  10. MySQL基础学习之函数

    数学函数 绝对值      abs() 圆周率      PI() 平方根 sqrt() 模除取余   mod(被除数,除数) 随机数      rand() 四舍五入    round(数字) 次方 ...