.net中关于异步性能测试的示例代码

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

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

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

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

.net中关于异步性能测试的示例代码.net中关于异步性能测试的示例代码

 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;        }    }

View Code

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

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方法的睡眠时间(毫秒),输入非数字内容退出:10Result:"Hello world,10"1000次 BIO(同步)测试(睡眠10 毫秒):耗时(秒):1.2860545,QPS:    777.571000次 AIO(异步)测试(睡眠10 毫秒):耗时(秒):0.4895946,QPS:   2042.51请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100Result:"Hello world,100"1000次 BIO(同步)测试(睡眠100 毫秒):耗时(秒):8.2769307,QPS:    120.821000次 AIO(异步)测试(睡眠100 毫秒):耗时(秒):0.5435111,QPS:   1839.89

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

 

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

.net中关于异步性能测试的示例代码.net中关于异步性能测试的示例代码

 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(()=> 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 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;        }    }

View Code

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

 public static async Task 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(()=> 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方法的睡眠时间(毫秒),输入非数字内容退出:10Result:Hello world,101000次 BIO(同步)测试(睡眠10 毫秒):耗时(秒):1.3031966,QPS:    767.341000次 AIO(异步)测试(睡眠10 毫秒):耗时(秒):0.026441,QPS:  37820.05请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100Result:Hello world,1001000次 BIO(同步)测试(睡眠100 毫秒):耗时(秒):9.8502858,QPS:    101.521000次 AIO(异步)测试(睡眠100 毫秒):耗时(秒):0.1149469,QPS:   8699.67请输入线程数:10000请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10Result:Hello world,1010000次 BIO(同步)测试(睡眠10 毫秒):耗时(秒):7.7966125,QPS:   1282.6110000次 AIO(异步)测试(睡眠10 毫秒):耗时(秒):0.083922,QPS: 119158.27请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100Result:Hello world,10010000次 BIO(同步)测试(睡眠100 毫秒):耗时(秒):34.3646036,QPS:    291.0010000次 AIO(异步)测试(睡眠100 毫秒):耗时(秒):0.1721833,QPS:  58077.64

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

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

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

 

以上就是.net中关于异步性能测试的示例代码的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1432605.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
.NET Core中遇到的一些坑的图文详解
上一篇 2025年12月17日 08:27:23
下一篇 2025年12月17日 08:27:40

相关推荐

  • 使用 JavaScript 将变量值显示在 <h1> 标签中

    本文旨在解决 JavaScript 中无法将变量值正确显示在 标签中的问题。我们将通过分析常见错误原因,提供清晰的代码示例,并介绍最佳实践,帮助开发者正确地使用 JavaScript 操作 DOM 元素,实现动态更新 标签内容的功能。 在 Web 开发中,经常需要使用 JavaScript 动态地更…

    2026年5月10日
    000
  • C#如何进行网络编程?Socket与TCP/IP通信编程实例详解

    C#通过Socket类实现TCP通信,首先服务器绑定IP和端口并监听,客户端发起连接,双方通过Send/Receive收发数据,最后关闭连接。 C# 进行网络编程主要依赖于 System.Net 和 System.Net.Sockets 命名空间,其中最核心的是使用 Socket 类实现基于 TCP…

    2026年5月10日
    000
  • C# 怎么使用 Serilog 或 NLog 记录日志_C# 日志记录框架使用指南

    Serilog和NLog是.NET中常用日志框架,Serilog支持结构化日志,配置简洁,适合集成Seq、Elasticsearch;NLog配置灵活,支持复杂规则,适用于企业级应用。两者均通过NuGet安装,配合配置文件或代码初始化,并通过ILogger接口写入日志,可根据项目需求选择其一。 在 …

    2026年5月10日
    000
  • C#怎么进行UDP通信 C# UdpClient实现UDP协议编程

    使用UdpClient类可简化C#中的UDP通信。1. 发送数据:创建UdpClient实例,调用Send()方法指定目标IP和端口,如向127.0.0.1:8888发送”Hello UDP!”;2. 接收数据:绑定端口(如8888),使用Receive()阻塞等待数据,通过…

    2026年5月10日
    100
  • 在 C++ 框架中实现极致性能的秘诀

    在 c++++ 框架中实现极致性能的秘诀:微优化代码:内联函数、去除冗余计算、使用现代编译器、使用 профилировщик。优化体系结构:多线程、分层缓存、减少锁争用、使用并行数据结构。管理资源:内存管理、网络优化、数据库访问、使用缓存。 在 C++ 框架中实现极致性能的秘诀 在当今快节奏的世界…

    2026年5月10日
    000
  • C# 异步流(async streams)的用法 – await foreach循环

    C# 8.0+ 引入异步流(IAsyncEnumerable)和 await foreach,支持非阻塞式异步枚举;可配合 CancellationToken 实现取消,需 C# 8.0+ 和兼容 SDK(如 net6.0)。 在 C# 8.0 及更高版本中,引入了异步流(async streams…

    2026年5月10日
    000
  • C#的System.IO.Pipelines是什么?如何实现高性能的流处理?

    System.IO.Pipelines通过PipeReader和PipeWriter减少内存分配与拷贝,高效处理流数据,适用于高吞吐、低延迟场景如网络通信和协议解析。 System.IO.Pipelines 是 C# 中用于高效处理流数据的一个库,特别适合高吞吐、低延迟的场景,比如网络通信、文件解析…

    2026年5月10日
    100
  • .NET中的仓储模式(Repository Pattern)是什么?如何解耦业务逻辑和数据访问?

    仓储模式是.NET中用于分离业务逻辑与数据访问的抽象层,通过定义如IUserRepository接口并结合依赖注入,实现对数据访问的具体技术解耦;业务逻辑仅依赖接口,可通过SqlUserRepository等具体实现操作数据库,而无需知晓底层细节;该模式提升可维护性、支持单元测试、降低耦合,并可配合…

    2026年5月10日
    000
  • C# 如何高效读取超大xml文件

    使用 XmlReader 流式读取超大 XML 文件,避免内存溢出。1. 通过 XmlReader 逐节点解析,仅读取所需数据;2. 遇到 Record 节点时提取 Id 属性及 Name 元素值;3. 可结合 ReadSubtree 对局部子树使用 LINQ to XML 解析;4. 设置 Xml…

    2026年5月10日
    000
  • .NET中的WPF是什么?如何使用MVVM模式来构建桌面应用?

    WPF是.NET的UI框架,使用XAML实现界面与逻辑分离,支持数据绑定、样式模板和MVVM模式,通过ViewModel暴露数据与命令,View绑定其属性与ICommand实现交互,提升可维护性。 WPF(Windows Presentation Foundation)是 .NET 框架中的一个用于…

    2026年5月10日
    000
  • .NET怎么在程序中执行一个外部exe文件

    使用System.Diagnostics.Process类可执行外部exe文件,通过Process.Start启动进程,支持简单调用和ProcessStartInfo配置参数、工作目录、窗口行为及输出重定向,需注意路径、权限和异常处理。 在 .NET 程序中执行外部 exe 文件,最常用的方式是使用…

    2026年5月10日
    000
  • 如何用C#实现数据库的跨平台迁移?使用EF Core工具?

    使用EF Core实现跨平台数据库迁移,需定义实体与DbContext,通过动态配置不同数据库提供程序,利用EF Core CLI生成并应用迁移,结合Fluent API处理数据库差异,确保结构与数据兼容。 要实现数据库的跨平台迁移,C# 中最常用且高效的方式是使用 Entity Framework…

    2026年5月10日
    000
  • C++ 函数性能优化与代码可维护性的权衡

    在c++++开发中,函数性能优化和代码可维护性需要权衡。优化方法包括:避免复制代码,使用函数和模板提高可维护性和效率。提高本地性,尽量在本地范围内访问变量,使用引用或指针。谨慎使用内联,避免代码膨胀。根据性能要求选择合适的算法和数据结构。避免不必要的对象创建。 C++ 函数性能优化与代码可维护性的权…

    2026年5月10日
    000
  • C#中什么是依赖注入 C# ASP.NET Core依赖注入(DI)的实现原理

    依赖注入是ASP.NET Core实现IoC的核心机制,通过外部容器在运行时将服务实例自动传递给类的构造函数,降低耦合并提升可测试性与维护性。传统方式中类内部直接new依赖导致紧耦合,而DI通过构造函数接收依赖接口,由框架注入具体实现,使业务逻辑与实现分离。ASP.NET Core内置轻量级容器,基…

    2026年5月10日
    000
  • 深入理解go.net/html:如何获取HTML节点的完整文本内容

    本教程详细介绍了如何使用go语言的`go.net/html`库解析html并准确提取html元素的内部文本内容。文章阐明了html节点树结构中`elementnode`与`textnode`的区别,并提供了一种通过递归遍历子节点来收集所有文本内容的通用方法,辅以示例代码和注意事项,帮助开发者高效处理…

    2026年5月10日
    000
  • 闭包在测试和调试方面的作用是什麼?

    闭包在测试和调试中的作用包括:隔离测试,防止外部变量影响结果。调试难以到达的变量,保持对变量的访问和修改。缓存数据,提升程序性能。 闭包在测试和调试中的作用 什么是闭包? 闭包是一个函数,它能访问它定义所在作用域之外的变量。闭包将这些外部变量保存在内存中,即使定义它们的函数已执行完毕。 闭包在测试和…

    2026年5月10日
    000
  • C#中如何监控数据库查询性能?使用什么工具?

    通过Stopwatch记录查询耗时,结合日志系统输出;2. 启用EF Core内置日志捕获SQL与执行时间;3. 生产环境使用Application Insights实现自动追踪与告警;4. 结合SQL Server Profiler、Extended Events等数据库工具分析性能瓶颈;5. 开…

    2026年5月10日
    000
  • .NET中接口(Interface)和抽象类(Abstract Class)的区别

    接口支持多实现,抽象类仅单继承;抽象类可包含字段和实现,接口主要用于定义行为规范;接口强调“能做什么”,抽象类表达“是什么”;修改接口影响所有实现,抽象类更易扩展。 接口(Interface)和抽象类(Abstract Class)在 .NET 中都用于实现多态和定义契约,但它们的设计目的和使用场景…

    2026年5月10日
    000
  • 优化Tkinter主题性能:解决UI卡顿与提升响应速度

    本文旨在探讨Tkinter应用中主题性能下降的问题,尤其是在Windows和macOS平台上使用图像密集型主题时。我们将分析导致UI卡顿的常见原因,并提供优化策略,包括选择高性能主题(如sv-ttk)、减少图像依赖,以及在必要时考虑其他现代GUI框架,以帮助开发者构建更流畅、响应更快的用户界面。 T…

    2026年5月10日
    000
  • C# 如何使用Channel – .NET中高性能的生产者消费者队列

    Channel 是 .NET 5+ 推荐的异步生产者-消费者通信原语,相比 Queue 和 BlockingCollection 更轻量、支持无锁操作、内置完成与取消感知,具备有界/无界模式以控制背压,Reader/Writer 可分离实现组件解耦,配合 TryRead 批处理与 WriteAsyn…

    用户投稿 2026年5月10日
    000

发表回复

登录后才能评论
关注微信