主頁 > .NET開發 > 一個例子形象的理解協程和執行緒的區別

一個例子形象的理解協程和執行緒的區別

2022-08-17 12:46:25 .NET開發

一個例子形象的理解協程和執行緒的區別

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("http://localhost:8500/api/test/TestGet?val=" + 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("http://localhost:8500/api/test/TestGet?val=" + 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的,維持在一定數量,直到請求完成,也就是說,測驗程序中,要占用一定數量的作業執行緒,

所以結論是什么?
協程在執行耗時請求時,不會占用執行緒(注意占用這個詞,它肯定是使用執行緒的,但不會在耗時請求程序中占用),在執行緒池中執行緒數量較少時,協程的性能比多執行緒好很多,想一想,要是IO操作、資料庫操作,存在一些慢查詢、超時的,如果你使用多執行緒,你的執行緒池就爆了,協程就不會(Talk is cheap, show me the code!),后面附上測驗,

WebAPI服務端補充說明

上面的測驗,服務端我忘了說了,服務端啟動服務前,我加了一行代碼ThreadPool.SetMinThreads(200, 200);,因為你測驗客戶端之前,服務端性能要跟上,不然測了個寂寞,
如果我把這行代碼刪掉,預熱后,再測:
測驗結果
可以看到差距只有2.5倍了!因為服務端執行緒數量此時是1秒增加1、2個執行緒,服務端性能跟不上,客戶端的異步請求自然也快不起來,

爆執行緒池測驗

測驗前修改:

  1. 把WebAPI介面中模擬執行耗時操作改成2000000毫秒,模擬服務端性能不行,反應慢,
  2. 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;
        }
    }
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/502087.html

標籤:C#

上一篇:MySQL

下一篇:一個例子形象的理解協程和執行緒的區別

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more