龙空技术网

一个例子形象的理解异步和多线程的区别

Java架构师鸨哥 837

前言:

目前咱们对“js foreach 异步”可能比较珍视,我们都需要剖析一些“js foreach 异步”的相关资讯。那么小编在网上汇集了一些关于“js foreach 异步””的相关文章,希望朋友们能喜欢,兄弟们一起来了解一下吧!

一个例子形象的理解异步和多线程的区别

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") };    }}
测试代码
using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Windows.Forms;using Utils;namespace AsyncDemo2{    public partial class Form1 : Form    {        private int n = 200;        public Form1()        {            InitializeComponent();            Task.Factory.StartNew(() =>            {                while (true)                {                    Thread.Sleep(100);                    ThreadPool.GetMaxThreads(out int w1, out int c1);                    ThreadPool.GetAvailableThreads(out int w2, out int c2);                    int w = w1 - w2;                    int c = c1 - c2;                    label1.BeginInvoke(new Action(() =>                    {                        label1.Text = string.Format("工作线程:{0} 异步线程:{1}", w, c);                    }));                }            }, TaskCreationOptions.LongRunning);        }        /// <summary>        /// 日志输出        /// </summary>        private void Log(string msg)        {            this.BeginInvoke(new Action(() =>            {                textBox1.AppendText(DateTime.Now.ToString("mm:ss.fff") + ":" + msg + "\r\n");            }));        }        /// <summary>        /// 异步请求        /// </summary>        private async Task ReqeustAsync(int val)        {            try            {                Log("异步  开始请求" + val);                string result = await HttpUtil.HttpGetAsync("; + val);                Log("异步  返回数据" + result + "  线程ID:" + Thread.CurrentThread.ManagedThreadId);            }            catch (Exception ex)            {                Log("出错:" + ex.Message);            }        }        /// <summary>        /// 在线程中同步请求        /// </summary>        private Task Request(int val)        {            return Task.Run(() =>            {                try                {                    Log("同步多线程  开始请求" + val);                    string result = HttpUtil.HttpGet("; + val);                    Log("同步多线程  返回数据" + result + "  线程ID:" + Thread.CurrentThread.ManagedThreadId);                }                catch (Exception ex)                {                    Log("出错:" + ex.Message);                }            });        }        //测试异步请求        private async void button3_Click(object sender, EventArgs e)        {            textBox1.Text = string.Empty;            Stopwatch sw = new Stopwatch();            List<Task> taskList = new List<Task>();            sw.Start();            for (int i = 0; i < n; i++)            {                Task t = ReqeustAsync(i);                taskList.Add(t);            }            foreach (Task t in taskList)            {                await t;            }            Log(n + "个异步请求完成,耗时:" + sw.Elapsed.TotalSeconds.ToString("0.000"));            sw.Stop();        }        //测试多线程同步请求        private void button4_Click(object sender, EventArgs e)        {            textBox1.Text = string.Empty;            Task.Run(() =>            {                List<Task> taskList = new List<Task>();                Stopwatch sw = new Stopwatch();                sw.Start();                for (int i = 0; i < n; i++)                {                    Task t = Request(i);                    taskList.Add(t);                }                Task.WaitAll(taskList.ToArray());                Log(n + "个多线程同步请求完成,耗时:" + sw.Elapsed.TotalSeconds.ToString("0.000"));                sw.Stop();            });        }    }}
测试结果

性能差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的,维持在一定数量,直到请求完成,也就是说,测试过程中,要占用一定数量的工作线程。

所以结论是什么?

异步在执行耗时请求时,不会占用线程,在线程池中线程数量较少时,异步的性能比多线程好很多,

WebAPI服务端补充说明

上面的测试,服务端我忘了说了,服务端启动服务前,我加了一行代码ThreadPool.SetMinThreads(200, 200);,因为你测试客户端之前,服务端性能要跟上,不然测了个寂寞。

如果我把这行代码删掉,预热后,再测:

可以看到差距只有2.5倍了!因为服务端线程数量此时是1秒增加1、2个线程,服务端性能跟不上,客户端的异步请求自然也快不起来。

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

标签: #js foreach 异步