一个例子形象的理解协程和线程的区别
Talk is cheap, show me the code! 所以,废话先不说,先上代码:
首先写一个WebAPI接口
- /// <summary>
- /// 测试接口
- /// </summary>
- [RoutePrefix("api/test")]
- public class TestController : ApiController
- {
- /// <summary>
- /// 测试GET请求
- /// </summary>
- /// <param name="val">测试参数</param>
- [HttpGet]
- [Route("TestGet")]
- public HttpResponseMessage TestGet(string val)
- {
- Thread.Sleep(200); //模拟执行耗时操作
- return new HttpResponseMessage { Content = new StringContent(val.ToString(), Encoding.UTF8, "text/plain") };
- }
- }
复制代码 测试代码
测试结果

性能差9倍!
把WebAPI接口中模拟执行耗时操作改成1000毫秒再测试,测试结果如下:

性能差10倍!
把Form1.cs构造函数中添加一行ThreadPool.SetMinThreads(20, 20);再测:

设置线程池中线程的最小数量为20后,性能差距缩小了,性能只差4倍!为什么?没有设置线程池最小数量时,大约每1秒增加1到2个线程,线程增加速度太慢了,不影响协程性能,协程只需要很少的线程数量,但影响多线程性能。
把Form1.cs构造函数中代码修改成ThreadPool.SetMinThreads(200, 200);再测:

当线程池中线程数量足够多时,性能差不多了!
结论
通过这个形象的例子,你体会到协程的好处了吗?
有人可能会说,你怎么不把WebAPI端改成异步试试?WebAPI端是模拟的操作,在没有外部操作(IO操作、数据库操作等),仅有数据计算时,WebAPI端改成异步没区别。
有一个截图中没有体验出来的,测试过程中,对于协程测试,工作线程和异步线程始终为0,我想异步线程应该是变化的,可能只是变化太快,看不出来。而多线程测试,测试过程中,我们可以看到工作线程的数量是大于0的,维持在一定数量,直到请求完成,也就是说,测试过程中,要占用一定数量的工作线程。
所以结论是什么?
协程在执行耗时请求时,不会占用线程(注意占用这个词,它肯定是使用线程的,但不会在耗时请求过程中占用),在线程池中线程数量较少时,协程的性能比多线程好很多。想一想,要是IO操作、数据库操作,存在一些慢查询、超时的,如果你使用多线程,你的线程池就爆了,协程就不会(Talk is cheap, show me the code!),后面附上测试。
WebAPI服务端补充说明
上面的测试,服务端我忘了说了,服务端启动服务前,我加了一行代码ThreadPool.SetMinThreads(200, 200);,因为你测试客户端之前,服务端性能要跟上,不然测了个寂寞。
如果我把这行代码删掉,预热后,再测:

可以看到差距只有2.5倍了!因为服务端线程数量此时是1秒增加1、2个线程,服务端性能跟不上,客户端的异步请求自然也快不起来。
爆线程池测试
测试前修改:
- 把WebAPI接口中模拟执行耗时操作改成2000000毫秒,模拟服务端性能不行,反应慢。
- ThreadPool.SetMinThreads(20, 20);客户端线程池最小线程数量设置为20,当然线程池越大越不容易爆,这里为了更快重现出来,所以设置小一点,注意,我可没有设置线程池上限!只是设置了下限。
测试视频:
注意看测试时工作线程数量:

说明:协程,不论什么时候点,都会有响应,当然可能后面点多了会报错,但即使报错,响应是有的。而多线程,后面点的,响应就很慢了。

你们可能会说你设置的线程池最小线程数量太小,改成ThreadPool.SetMinThreads(200, 200);,再测:

注意看工作线程数量!
附
WebAPI服务启动代码:- protected override void OnStart(string[] args)
- {
- ThreadPool.SetMinThreads(200, 200);
- int port = int.Parse(ConfigurationManager.AppSettings["WebApiServicePort"]);
- StartOptions options = new StartOptions();
- options.Urls.Add("http://127.0.0.1:" + port);
- options.Urls.Add("http://localhost:" + port);
- options.Urls.Add("http://+:" + port);
- WebApp.Start<Startup>(options);
- LogUtil.Log("Web API 服务 启动成功");
- }
复制代码 HttpUtil代码:- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- namespace Utils
- {
- /// <summary>
- /// Http上传下载文件
- /// </summary>
- public class HttpUtil
- {
- /// <summary>
- /// HttpGet
- /// </summary>
- /// <param name="url">url路径名称</param>
- /// <param name="cookie">cookie</param>
- public static async Task<string> HttpGetAsync(string url, CookieContainer cookie = null, WebHeaderCollection headers = null)
- {
- // 设置参数
- HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
- request.CookieContainer = cookie;
- request.Method = "GET";
- request.ContentType = "text/plain;charset=utf-8";
- request.Timeout = Timeout.Infinite;
- if (headers != null)
- {
- foreach (string key in headers.Keys)
- {
- request.Headers.Add(key, headers[key]);
- }
- }
- //发送请求并获取相应回应数据
- HttpWebResponse response = (HttpWebResponse)(await request.GetResponseAsync());
- //直到request.GetResponse()程序才开始向目标网页发送Post请求
- Stream instream = response.GetResponseStream();
- StreamReader sr = new StreamReader(instream, Encoding.UTF8);
- //返回结果网页(html)代码
- string content = await sr.ReadToEndAsync();
- instream.Close();
- return content;
- }
- /// <summary>
- /// HttpGet
- /// </summary>
- /// <param name="url">url路径名称</param>
- /// <param name="cookie">cookie</param>
- public static string HttpGet(string url, CookieContainer cookie = null, WebHeaderCollection headers = null)
- {
- // 设置参数
- HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
- request.CookieContainer = cookie;
- request.Method = "GET";
- request.ContentType = "text/plain;charset=utf-8";
- request.Timeout = Timeout.Infinite;
- if (headers != null)
- {
- foreach (string key in headers.Keys)
- {
- request.Headers.Add(key, headers[key]);
- }
- }
- //发送请求并获取相应回应数据
- HttpWebResponse response = (HttpWebResponse)request.GetResponse();
- //直到request.GetResponse()程序才开始向目标网页发送Post请求
- Stream instream = response.GetResponseStream();
- StreamReader sr = new StreamReader(instream, Encoding.UTF8);
- //返回结果网页(html)代码
- string content = sr.ReadToEnd();
- instream.Close();
- return content;
- }
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |