很久没有写博客了,今年做的产品公司这两天刚刚开了发布会,稍微清闲下来,想想我们做的产品还有没有性能优化空间,于是想到了.Net的异步可以优化性能,但到底能够提升多大的比例呢?恰好有一个朋友正在做各种语言的异步性能测试(有关异步和同步的问题,请参考客《AIO与BIO接口性能对比》),于是我今天写了一个C#的测试程序。

首先,建一个 ASP.NET MVC WebAPI项目,在默认的控制器 values里面,增加两个方法:

 // GET api/values?sleepTime=10[HttpGet]public async Task<string> ExecuteAIO(int sleepTime){await Task.Delay(sleepTime);return  "Hello world,"+ sleepTime;}[HttpGet]// GET api/values?sleepTime2=10public string ExecuteBIO(int sleepTime2){System.Threading.Thread.Sleep(sleepTime2);return "Hello world," + sleepTime2;}

然后,建立一个控制台程序,来测试这个web API:

 class Program{static void Main(string[] args){Console.WriteLine("按任意键开始测试 WebAPI:http://localhost:62219/api/values?sleepTime={int}");Console.Write("请输入线程数:");int threadNum = 100;int.TryParse(Console.ReadLine(), out threadNum);while (Test(threadNum)) ;Console.ReadLine();Console.ReadLine();}private static bool Test(int TaskNumber){Console.Write("请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:");string input = Console.ReadLine();int SleepTime = 50;if (!int.TryParse(input, out SleepTime))return false;HttpClient client = new HttpClient();client.BaseAddress = new Uri("http://localhost:62219/");var result = client.GetStringAsync("api/values?sleepTime=" + input).Result;Console.WriteLine("Result:{0}", result);//int TaskNumber = 1000;Console.WriteLine("{0}次 BIO(同步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();sw.Start();Task[] taskArr = new Task[TaskNumber];for (int i = 0; i < TaskNumber; i++){Task task = client.GetStringAsync("api/values?sleepTime2=" + SleepTime);taskArr[i] = task;}Task.WaitAll(taskArr);sw.Stop();double useTime1 = sw.Elapsed.TotalSeconds;Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime1, TaskNumber/useTime1);sw.Reset();Console.WriteLine("{0}次 AIO(异步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);sw.Start();for (int i = 0; i < TaskNumber; i++){Task task = client.GetStringAsync("api/values?sleepTime=" + SleepTime);taskArr[i] = task;}Task.WaitAll(taskArr);sw.Stop();double useTime2 = sw.Elapsed.TotalSeconds;Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime2, TaskNumber / useTime2);return true;}}

其实主要是下面几行代码:

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:62219/");
var result = client.GetStringAsync("api/values?sleepTime=" + input).Result;

注意,你可能需要使用Nuget添加下面这个包:

Microsoft.AspNet.WebApi.Client

最后,运行这个测试,结果如下:

按任意键开始测试 WebAPI:http://localhost:62219/api/values?sleepTime={int}
请输入线程数:1000
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10
Result:"Hello world,10"
1000次 BIO(同步)测试(睡眠10 毫秒):
耗时(秒):1.2860545,QPS:    777.57
1000次 AIO(异步)测试(睡眠10 毫秒):
耗时(秒):0.4895946,QPS:   2042.51
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100
Result:"Hello world,100"
1000次 BIO(同步)测试(睡眠100 毫秒):
耗时(秒):8.2769307,QPS:    120.82
1000次 AIO(异步)测试(睡眠100 毫秒):
耗时(秒):0.5435111,QPS:   1839.89

本来想尝试测试10000个线程,但报错了。

上面的测试结果,QPS并不高,但由于使用的是IISExpress,不同的Web服务器软件性能不相同,所以还得对比下进程内QPS结果,于是新建一个控制台程序,代码如下:

 class Program{static void Main(string[] args){Console.WriteLine("按任意键开始测试 ");Console.Write("请输入线程数:");int threadNum = 100;int.TryParse(Console.ReadLine(), out threadNum);while (Test(threadNum)) ;Console.ReadLine();Console.ReadLine();}private static bool Test(int TaskNumber){Console.Write("请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:");string input = Console.ReadLine();int SleepTime = 50;if (!int.TryParse(input, out SleepTime))return false;var result = ExecuteAIO(SleepTime).Result;Console.WriteLine("Result:{0}", result);//int TaskNumber = 1000;Console.WriteLine("{0}次 BIO(同步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();sw.Start();Task[] taskArr = new Task[TaskNumber];for (int i = 0; i < TaskNumber; i++){Task task = Task.Run<string>(()=> ExecuteBIO(SleepTime));taskArr[i] = task;}Task.WaitAll(taskArr);sw.Stop();double useTime1 = sw.Elapsed.TotalSeconds;Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime1, TaskNumber / useTime1);sw.Reset();Console.WriteLine("{0}次 AIO(异步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);sw.Start();for (int i = 0; i < TaskNumber; i++){Task task = ExecuteAIO(SleepTime);taskArr[i] = task;}Task.WaitAll(taskArr);sw.Stop();double useTime2 = sw.Elapsed.TotalSeconds;Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime2, TaskNumber / useTime2);return true;}public static async Task<string> ExecuteAIO(int sleepTime){await Task.Delay(sleepTime);return "Hello world," + sleepTime;}public static string ExecuteBIO(int sleepTime2){System.Threading.Thread.Sleep(sleepTime2);//不能在非异步方法里面使用 Task.Delay,否则可能死锁//Task.Delay(sleepTime2).Wait();return "Hello world," + sleepTime2;}}

注意,关键代码只有下面两个方法:

 public static async Task<string> ExecuteAIO(int sleepTime){await Task.Delay(sleepTime);return "Hello world," + sleepTime;}public static string ExecuteBIO(int sleepTime2){System.Threading.Thread.Sleep(sleepTime2);//不能在非异步方法里面使用 Task.Delay,否则可能死锁//Task.Delay(sleepTime2).Wait();return "Hello world," + sleepTime2;}

这两个方法跟WebAPI的测试方法代码是一样的,但是调用代码稍微不同:

同步调用:

 Task[] taskArr = new Task[TaskNumber];for (int i = 0; i < TaskNumber; i++){Task task = Task.Run<string>(()=> ExecuteBIO(SleepTime));taskArr[i] = task;}Task.WaitAll(taskArr);

异步调用:

 for (int i = 0; i < TaskNumber; i++){Task task = ExecuteAIO(SleepTime);taskArr[i] = task;}Task.WaitAll(taskArr);

可见,这里测试的时候,同步和异步调用,客户端代码都是使用的多线程,主要的区别就是异步方法使用了 async/await 语句。

下面是非Web的进程内异步多线程和同步多线程的结果:

请输入线程数:1000
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10
Result:Hello world,10
1000次 BIO(同步)测试(睡眠10 毫秒):
耗时(秒):1.3031966,QPS:    767.34
1000次 AIO(异步)测试(睡眠10 毫秒):
耗时(秒):0.026441,QPS:  37820.05
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100
Result:Hello world,100
1000次 BIO(同步)测试(睡眠100 毫秒):
耗时(秒):9.8502858,QPS:    101.52
1000次 AIO(异步)测试(睡眠100 毫秒):
耗时(秒):0.1149469,QPS:   8699.67请输入线程数:10000
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10
Result:Hello world,10
10000次 BIO(同步)测试(睡眠10 毫秒):
耗时(秒):7.7966125,QPS:   1282.61
10000次 AIO(异步)测试(睡眠10 毫秒):
耗时(秒):0.083922,QPS: 119158.27
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100
Result:Hello world,100
10000次 BIO(同步)测试(睡眠100 毫秒):
耗时(秒):34.3646036,QPS:    291.00
10000次 AIO(异步)测试(睡眠100 毫秒):
耗时(秒):0.1721833,QPS:  58077.64

结果表示,.NET程序开启10000个任务(不是10000个原生线程,需要考虑线程池线程),异步方法的QPS超过了10万,而同步方法只有1000多点,性能差距还是很大的。

注:以上测试结果的测试环境是

Intel i7-4790K CPU,4核8线程,内存 16GB,Win10 企业版

总结:

不论是普通程序还是Web程序,使用异步多线程,可以极大的提高系统的吞吐量。

后记:

感谢网友“双鱼座” 的提示,我用信号量和都用线程Sleep的方式,对同步和异步方法进行了测试,结果如他所说,TPL异步方式,开销很大,下面是测试数据:

使用 semaphoreSlim 的情况:请输入线程数:1000
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10
Result:Hello world,10
1000次 BIO(同步)测试(睡眠10 毫秒):
耗时(秒):1.2486964,QPS:    800.84
1000次 AIO(异步)测试(睡眠10 毫秒):
耗时(秒):10.5259443,QPS:     95.00
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100
Result:Hello world,100
1000次 BIO(同步)测试(睡眠100 毫秒):
耗时(秒):12.2754003,QPS:     81.46
1000次 AIO(异步)测试(睡眠100 毫秒):
耗时(秒):100.5308431,QPS:      9.95
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:1000
Result:Hello world,1000
1000次 BIO(同步)测试(睡眠1000 毫秒):
耗时(秒):54.0055828,QPS:     18.52
1000次 AIO(异步)测试(睡眠1000 毫秒):
耗时(秒):1000.4749124,QPS:      1.00

使用线程 Sleep的代码改造:

  public static async Task<string> ExecuteAIO(int sleepTime){//await Task.Delay(sleepTime);//return "Hello world," + sleepTime;//await Task.Delay(sleepTime);//semaphoreSlim.Wait(sleepTime);System.Threading.Thread.Sleep(sleepTime);return await Task.FromResult("Hello world," + sleepTime);}public static string ExecuteBIO(int sleepTime2){System.Threading.Thread.Sleep(sleepTime2);//semaphoreSlim.Wait(sleepTime2);//不能在非异步方法里面使用 Task.Delay,否则可能死锁//Task.Delay(sleepTime2).Wait();return "Hello world," + sleepTime2;}

运行结果如下:

请输入线程数:1000
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10
Result:Hello world,10
1000次 BIO(同步)测试(睡眠10 毫秒):
耗时(秒):1.3099217,QPS:    763.40
1000次 AIO(异步)测试(睡眠10 毫秒):
耗时(秒):10.9869045,QPS:     91.02
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100
Result:Hello world,100
1000次 BIO(同步)测试(睡眠100 毫秒):
耗时(秒):8.5861461,QPS:    116.47
1000次 AIO(异步)测试(睡眠100 毫秒):
耗时(秒):100.9829406,QPS:      9.90
请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:1000
Result:Hello world,1000
1000次 BIO(同步)测试(睡眠1000 毫秒):
耗时(秒):27.0158904,QPS:     37.02
1000次 AIO(异步)测试(睡眠1000 毫秒):

在每次睡眠1秒的异步方法测试中,很久都没有出来结果,不用考虑,QPS肯定低于一秒了。

经验教训:

在异步方法中,不要使用 Thread.Sleep;在同步方法中,不要使用Task.Delay ,否则可能出现线程死锁,结果难出来。

【转】.net异步性能测试(包括ASP.NET MVC WebAPI异步方法)相关推荐

  1. 我使用Asp.net MVC WebAPI支持OData协议进行分页操作的笔记(第二篇)

    在阅读这篇文章的时候,我想你已经看完第一篇文章啦·也有可能跟我一样,现在正在使用它Asp.net WebAPI为我们干活儿.可能是服务分页查询接口,也可能是其它操作,遇到了一些小问题.有问题,那咱就来 ...

  2. Asp.net MVC 4 异步方法

    前面一篇文章我们介绍了Asp.net MVC 3下异步操作.今天我们来看一下,同样功能在 Asp.net MVC 4 下的实现,基于.net framework 4.5 下的async支持,让我们的代 ...

  3. ASP.NET MVC WebAPI 资源整理

    注:这是收集给公司同事学习的资料,入门级别的. 使用ASP.Net WebAPI构建REST服务(一)--简单的示例 http://blog.csdn.net/mengzhengjie/article ...

  4. Asp.Net MVC WebAPI的创建与前台Jquery ajax后台HttpClient调用详解

    1.什么是WebApi,它有什么用途? Web API是一个比较宽泛的概念.这里我们提到Web API特指ASP.NET MVC Web API.在新出的MVC中,增加了WebAPI,用于提供REST ...

  5. 我使用Asp.net MVC WebAPI支持OData协议进行分页操作的笔记(第一篇)

    OData协议.多么牛B的技术. 传统的分页写习惯了,最近项目中,用到了 Asp.net WebAPI 2.0来做数据交互接口.至于为什么要使用WebAPI,我想只要是对OData协议有了解的朋友.只 ...

  6. ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml)

    我们都知道在使用WebApi的时候Controller会自动将Action的返回值自动进行各种序列化处理(序列化为json,xml等),但是如果Controller的自动序列化后的结果不是我们想要的该 ...

  7. Asp.net MVC WebApi Response AOP_se7en3_新浪博客

    WebApi项目需要在输出Json时对Json进行编码处理.考虑使用MVC 的AOP 查阅文档需要HttpMessageHandle 新建类CryptDelegatingHandler public ...

  8. ASP.NET MVC WebAPI实现文件批量上传

    工作中学习,学习中记录~~~~~~ 最下面附上demo 任务需求:需要做一个apI接口让C#的程序调用实现批量文件的上传. 难度: 没有做过通过API上传文件的...之前做过网站前后台上传. 首先当然 ...

  9. C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(上)

    译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# ...

最新文章

  1. 【rnnoise源码分析】biquad滤波器
  2. 编译nginx时的两个报错
  3. 一个小程序:图片代替鼠标移动
  4. python彩色图像如何进行高斯滤波ValueError: correlate2d inputs must both be 2-D arrays解决方法
  5. Android处理崩溃的一些实践
  6. VTK:图片之ImageToPolyDataFilter
  7. 使用Httpclient来替代客户端的jsonp跨域解决方案
  8. JavaSE学习之IO流使用技巧
  9. 一企业彻底实现金融风险数字化,节约人力超4000小时
  10. 《汇编语言》王爽—实验五详解
  11. POJ - 3461 (kmp)
  12. 【数据处理】奇异值分解(SVD) 数据降噪的python实现
  13. ios首次加载web_iOS预加载Web页面方案
  14. rabbitmq安装erlang,报错configure: error: /bin/sh ‘/app/otp_src_20.2/erts/configure‘ failed for erts
  15. 可能最详细的教程,新手如何获取Zcash钱包(ZEC)官方地址的方法
  16. 创可贴的 ROS PX4 自主飞行无人机 学习笔记(3)
  17. PHP学习笔记:环境变量
  18. 【PaddleOCR】一、PaddleOCR安装、测试(Win10)
  19. 计算机网络技术日趋完善并走向,计算机应用基础 10
  20. Python中的True和False详解

热门文章

  1. Spring Boot----Dubbo
  2. MySQL和SQL Server数据库基本语句总结(二)
  3. 好文推荐系列--------(3)GruntJS 在线重载 提升生产率至新境界
  4. Hadoop 开源调度系统zeus(二)
  5. 一个伟大计划终于完成了(粉丝联盟网正式上线了)
  6. boost helloworlld
  7. 使用MvcContrib的FormHelper
  8. 二维凸包 Graham's Scan
  9. 【插件介绍】Lombok
  10. 南京二本有什么计算机学校,南京有什么好的二本学校?