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

很久没有写博客了,今年做的产品公司这两天刚刚开了发布会,稍微清闲下来,想想我们做的产品还有没有性能优化空间,于是想到了.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 ,否则可能出现线程死锁,结果难出来。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437412.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

win7关机快捷键_电脑快捷键大全(上)

Windows快捷键1单独按Windows&#xff1a;显示或隐藏“开始”功能表WindowsBREAK&#xff1a;显示“系统属性“对话框WindowsD显示桌面或回复桌面Windows M最小化所有窗口WindowsShiftM&#xff1a;还原最小化的窗口CrtlShiftN:新建文件夹WindowsE&#xff1a;打开“我的电脑”…

【转】ASP.NET Web API 使用Swagger生成在线帮助测试文档,支持多个GET

以下为教程: 在现有webapi项目中,nuget安装以下两个插件 swagger.net.ui swashbuckle 安装完毕后可以卸载Swagger.NET,此处不需要! 安装完毕后屏蔽以下代码 直接运行调试 在浏览器的目录后面加上/swagger即可跳转到swagger调试页 此时如果没有注释. 项目属性里添加xml注释…

idea提示“ cannot access xxxxxxxx.class“的解决方法,idea的bug

同一个包下的public类使用报错&#xff0c;应该是idea的bug&#xff1a; file -> Invalidate Caches / Restart

电脑屏保在哪里设置_手机屏保调成绿色能护眼?真的吗?

说到护眼&#xff0c;你首先想到的是什么颜色&#xff1f;估计90%的人都会不自觉的想到绿色&#xff01;因为从小家长和老师都会跟我们说&#xff1a;眼睛累了就多看窗户外的绿色植物。久而久之&#xff0c;一说到护眼&#xff0c;大家脑海里第一个想到的就是绿色。你会看到不少…

电脑休眠和睡眠的区别_关机、睡眠、休眠的区别

都知道电脑有关机、睡眠、休眠三种休息模式&#xff0c;但是后两个选项使用的人非常少&#xff0c;大多数人每次用完电脑都会选择立即「关机」。有人是为了让电脑「休息」&#xff0c;有人是为了低碳精神&#xff0c;还有人是因为下班不关电脑要罚款&#xff0c;不管是什么原因…

SpringCloud:学习Docker安装zookeeper,注册服务

1.没镜像就拉取镜像 dockerhub中查看版本 官网 docker pull zookeeper:3.4.14 不加版本号也行&#xff0c;默认拉取最新版 创建并启动容器 docker run -p 2181:2181 --privilegedtrue --name zookeeper01 -d zookeeper –privilegedtrue 容器内用户开启root权限 docker ps…

win10固态硬盘分区 整数_惠普HP笔记本Win10改Win7系统教程

惠普HP笔记本和台式机目前都预装的Win10系统&#xff0c;当然Win7旗舰版才是很多用户喜欢的&#xff0c;不过换装Win7有很多方面比较麻烦&#xff0c;如BIOS设置、U盘启动及方分方面都是很多用户不熟悉的&#xff0c;这里小编就详细分享下惠普笔记本Win10改Win7系统教程(BIOS设…

SpringCloud:学习 Docker安装Consul,注册服务

1.拉取镜像 docker pull consul 2.启动容器 docker run -d -p 8500:8500 --restartalways --nameconsul consul:latest agent -server -bootstrap -ui -node1 -client0.0.0.0 创建容器时没有添加参数 --restartalways &#xff0c;导致的后果是&#xff1a;当 Docker 重启时…

【转】WebApi 身份认证解决方案:Basic基础认证

参考路径&#xff1a;https://www.cnblogs.com/landeanfen/p/5287064.html 前言&#xff1a;最近&#xff0c;讨论到数据库安全的问题&#xff0c;于是就引出了WebApi服务没有加任何验证的问题。也就是说&#xff0c;任何人只要知道了接口的url&#xff0c;都能够模拟http请求去…

LeetCode每日打卡 - 汉明距离

位异或运算&#xff08;^&#xff09; 运算规则是&#xff1a;两个数转为二进制&#xff0c;然后从高位开始比较&#xff0c;如果相同则为0&#xff0c;不相同则为1。 比如&#xff1a;8^11. 8转为二进制是1000&#xff0c;11转为二进制是1011.从高位开始比较得到的是&#xff…

【转】Task和async/await详解

一、什么是异步 同步和异步主要用于修饰方法。当一个方法被调用时&#xff0c;调用者需要等待该方法执行完毕并返回才能继续执行&#xff0c;我们称这个方法是同步方法&#xff1b;当一个方法被调用时立即返回&#xff0c;并获取一个线程执行该方法内部的业务&#xff0c;调用者…

LeetCode每日打卡 - 反转每对括号间的子串

题解中有个更好的思路&#xff0c;stack存入的不是(的坐标&#xff0c;存入每次到左括号的字符串&#xff0c;拿到右括号就对其进行反转&#xff0c;更妙一些。 import java.util.Stack; class Solution {public String reverseParentheses(String s) {Stack<Integer> s…

iar stm32_树莓派玩转STM32开发(一)——介绍篇

01—树莓派树莓派(Raspberry Pi)听起来让人流口水&#xff0c;但它的确不是吃的(身为非吃货的我第一次也以为它是食物……)。树莓派是基于ARM架构的Linux卡片电脑&#xff0c;由英国树莓派基金会开发&#xff0c;目的是以低价硬件以及自由软件来促进学校的基本电脑科学教育。树…

JavaScript面向对象的理解

前言 1. 本文默认阅读者已有面向对象的开发思想&#xff0c;最好是使用过c、java&#xff0c;本人Java不太熟悉&#xff0c;所以例子都是用C来写的。 2. 本人不是专业网站开发人员&#xff0c;接触javascript一年多&#xff0c;自己也编写调试了一些代码&#xff0c;本文完全根…

【转】C# 彻底搞懂async/await

关键&#xff1a; 异步方法&#xff1a;在执行完成前立即返回调用方法&#xff0c;在调用方法继续执行的过程中完成任务。 async/await 结构可分成三部分&#xff1a; &#xff08;1&#xff09;调用方法&#xff1a;该方法调用异步方法&#xff0c;然后在异步方法执行其任务的…

LeetCode每日打卡 - 汉明距离总和

有点慢&#xff0c;两层循环也可以完成&#xff0c;就是换个方向&#xff0c;外层遍历32大小的bits数组&#xff0c;里层遍历nums的数字每次右移一位&#xff0c;计算方法类似。 class Solution {public int totalHammingDistance(int[] nums) {//int 是4byte &#xff0c; 每…

python编写代码_用 Python 编写干净、可测试、高质量的代码

用 Python 编写干净、可测试、高质量的代码Noah Gift 2010 年 12 月 20 日发布简介 编写软件是人所承担的最复杂的任务之一。AWK 编程语言和 "K and R C" 的作者之一 Brian Kernigan 在 Software Tools 一书中总结了软件开发的真实性质&#xff0c;他说&#xff0c;“…

阿里云服务器被[kthreaddi]挖矿病毒攻击

首先我根本https://blog.csdn.net/weixin_41599103/article/details/115403332这个博客试了下并没有成功&#xff0c;所以应该是被侵入的程序不一样 先去阿里云里看一下详情 明确告诉了是通过docker被攻击了&#xff0c;先将wordpress容器停止并删除容器和镜像 kill掉进行&am…

python canvas画弧度_python画一朵玫瑰给你

听说 python 还能画画&#xff1f;是的&#xff0c;今天亲手画一朵玫瑰给你。turtle 是 python 中一个强大的绘制图像的库&#xff0c;可以用来绘制各种图像&#xff0c;使用起来很方便。但是需要计算各种角度、距离等&#xff0c;所以要想完成一幅复杂的图像还是有一定的难度的…

通俗彻底解读批处理的延迟变量

先说环境变量 一个批处理可以看成是一个程序&#xff0c;它的环境变量就是自己的全局变量。操作系统可以看成是一个大程序&#xff0c;它的环境变量同样也是全局变量&#xff0c;只不过为了和程序的环境变量区别&#xff0c;就叫做全局环境变量了&#xff0c;既然为了区别&…