非同步程式設計:認識程序、執行緒、多執行緒(一)

C#知識系列

程序:一個程式執行起來所占用的全部計算資源總合。

關於執行緒:

  1. 程序執行的最小單位
  2. 任何操作都由執行緒完成
  3. 是依賴在程序存在,一個程序可以包含很多執行緒,有自己的計算資源
  4. 系統分配的CPU時間基本單位,程序第一個執行緒,為主執行緒

多執行緒:是實現執行緒並發執行的技術。

多執行緒優點:同時完成多個job,可以讓占用大量時間的任務或當前
                         沒進行的任務定期處理讓給別的任務,可以隨時停止
                         任務,可設定每個job的優先級,優化程式效能

多執行緒缺點:

  1、 内存占用:  執行緒需要佔用內存,執行緒越多,占用内存也越多(每個執行
                                  緒都需要建立堆疊空間,多執行緒有時需要切換時間片)。

  2、 管理協調:多執行緒需要協調和管理,所以需要占用CPU時間以便追蹤執行緒,
                                執行緒太多會導致控制太複雜。

  3、 資源共享:執行緒之間對共享資源會相互影響,必須解決共享資源問題。
 

關於C#多執行緒,EX:Thread是C#語言對執行緒的封裝。

同步方法:計算完成處理後,進入下一行。

EX:如果eddie要邀請阿龍去吃飯,但是阿龍要忙,那就要等阿龍完成再一起去吃飯。
 

非同步方法:只發送請求處理,不會等待處理完成,進入下一行,非阻塞。

EX:如果eddie要邀請阿龍去吃飯,但是阿龍要忙,eddie先去吃飯,阿龍忙完了自己去吃飯。

 非同步多執行緒:thread pool task

以下是多執行緒和非同步的狀況




接者來看畫面和程式

先設定專案啟用主控台

畫面

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 同步方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSync_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****************btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            for (int i = 0; i < 5; i++)
            {
                string name = string.Format($"btnSync_Click_{i}");
                this.DoSomethingLong(name);
            }

            Console.WriteLine($"****************btnSync_Click   End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");


        }
        /// <summary>
        /// 異步方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAsync_Click(object sender, EventArgs e)
        {
            Console.WriteLine("");
             Console.WriteLine($"****************btnAsync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            Action<string> action = this.DoSomethingLong;

            //for (int i = 0; i < 5; i++)
            //{
            //    string name = string.Format($"btnAsync_Click_{i}");
            //    action.BeginInvoke(name, null, null);
            //}
            action.Invoke("btnAsync_Click_1");
            action("btnAsync_Click_2");
            //非同步多執行緒
            action.BeginInvoke("btnAsync_Click_3", null, null);
            Console.WriteLine($"****************btnAsync_Click End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        }

        private void DoSomethingLong(string name)
        {
            Console.WriteLine($"****************DoSomethingLong Start  {name}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            long lResult = 0;
            for (int i = 0; i < 100000000; i++)
            {
                lResult += i;
            }
            Thread.Sleep(2000);

            Console.WriteLine($"****************DoSomethingLong   End  {name}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");

        }
    }

當你在運行同步方法的時候,就會UI卡死,執行btnSync_Click就會卡死概念。

同步事件:主執行緒(UI部分),忙者計算、畫面卡死

非同步的話就不一樣嚕
非同步:主要執行緒丟任務給子執行緒完成,改善用戶體驗,不至於卡死,例如大量EMAIL寄信通知,就可以給非同步去做。
 


        同步方法比較慢,因為只有一個執行緒計算,異步方法快,可以用多個執
行緒併發計算,多執行緒就是用資源來換效能。

多執行緒使用時機:

      查詢DB或讀取硬碟文件或數據計算,可以用多執行緒下效能優化?
A:可以、因為多任務可以並行,但是多執行緒不是越多越好,因為資源有限,調度有消耗,多執行緒盡量避免使用
 

接者微調程式來看一下檢視程式結果
 

        private void btnAsync_Click(object sender, EventArgs e)
        {
            Console.WriteLine("");
            Console.WriteLine($"****************btnAsync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            Action<string> action = this.DoSomethingLong;

            for (int i = 0; i < 5; i++)
            {
                string name = string.Format($"btnAsync_Click_{i}");
                action.BeginInvoke(name, null, null);
            }
            Console.WriteLine($"****************btnAsync_Click End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        }

接者我們在迴圈加個0,來實驗CPU的效果和成效

        private void DoSomethingLong(string name)
        {
            Console.WriteLine($"****************DoSomethingLong Start  {name}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            long lResult = 0;
                         //修改前100000000 
            for (int i = 0; i < 1000000000; i++)
            {
                lResult += i;
            }
            Thread.Sleep(2000);
            Console.WriteLine($"****************DoSomethingLong   End  {name}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
        }



從上圖來看同步方法的秒數消耗十幾秒、CPU曲線調用性是屬於穩定的

如果是下圖是非同步的話


那CPU的曲線就會過高,異步就會用資源來換取速度

        同步方法慢、只有一個執行緒執行處理,異步方法快,因為資源換時間,但是資源可
能會不夠,多執行緒也有管理成本不是開越多越好,必須多個獨立任務可以同時運行。

從上圖和下圖來看,下圖五個同時調用,啟動的時候在跟OS調用資源時候,它是無序(啟動/結束),執行時間不確定

老E隨手寫