iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
Software Development

玩轉C# 進階學習之旅系列 第 19

玩轉C#之【執行序-實際實作】

介紹

定義

處理序(Process) (大陸:進程): 一個程序運行時,占用全部計算資源的總和

執行緒(Thread) (大陸:線程):是作業系統能夠進行運算排程的最小單位。 大部分情況下,它被包含在行程之中,是行程中的實際運作單位。

C#多線程和異步(一)——基本概念和使用方法

執行緒帶來的負擔

Windows 每建立一條執行緒,須為它配置大約 1MB 左右的記憶體,其中包含執行緒核心物件、環境區塊(Thread Environment Block)、使用者模式堆疊、核心模式堆疊等等。這是記憶體空間的額外負擔。

Thread

C# 1.0 1.1 的多執行緒方法

方法:查詢目前執行緒的ID

Thread.CurrentThread.ManagedThreadId

演示用的函示

        private void doSomething(string name)
        {
            Console.WriteLine($"doSomething {name}Start 執行緒:{Thread.CurrentThread.ManagedThreadId}");
            long lRestul = 0;
            for (int i = 0; i < 1000000; i++)
            {
                lRestul++;
            }
            Thread.Sleep(2000);
            Console.WriteLine($"doSomething {name}End 執行緒:{Thread.CurrentThread.ManagedThreadId}");
        }

建立執行緒、並啟動執行

我們可以從Thread建構式中看出他需要一個ThreadStart型別的參數

public Thread(ThreadStart start);

可以再看出ThreadStart 是一個委派的型別

public delegate void ThreadStart();

 // Action action = () => this.doSomething("button3");
 ThreadStart threadStart = () => this.doSomething("button3");//委派
 //Thread thread =new Thread(action);
 Thread thread = new Thread(threadStart);
 thread.Start();//啟動

執行緒暫停、喚醒、銷毀

舊方法不建議使用

 thread.Suspend();//執行緒暫停  舊方法-被遺棄了-不建議使用-容易造成死鎖
 thread.Resume();//執行緒喚醒   舊方法-被遺棄了-不建議使用

不建議使用,它是利用拋出例外的方式,所以需要用try catch包住

            try
            {
                thread.Abort();//銷毀,方式是拋棄異常 也不建議 不一定及時
            }
            catch (Exception)
            {
                Thread.ResetAbort();//取消異常
            }

執行緒等待

可以使用以下兩種方式,解決執行緒等待

ThreadState狀態介紹
1.主執行緒判斷子執行緒的狀態是否為停止,如果還沒停止就讓主執行緒sleep

      while (thread.ThreadState != ThreadState.Stopped)
            {
                Thread.Sleep(100);//當前執行緒 休息100ms
            }

2.使用Join,當前執行緒等待thread完成

   //執行緒等待
   thread.Join(500);//最多等待500
   Console.WriteLine("最多等待500ms");
   thread.Join();//當前執行緒等待thread完成

Thread的特點IsBackground

所有的執行緒都是默認前台,就是當子執行緒任務完成之後,程式才可以退出
只有Thread可以設定子執行緒是前台或後台

 //IsBackground 是Thread的特點 只有thread可以設定前台,後台
 Console.WriteLine(thread.IsBackground);
 //默認是前台線程,啟動後一定要先完成任務的,阻止進程退出
 thread.IsBackground = true;//指定後台線程 隨者程式退出

設定執行緒優先級別

沒什麼作用

  thread.Priority = ThreadPriority.Highest;//設定線程優先級別
  //CPU會依優先執行Highest 不代表Highest 就最先結束

結論:

thread已經越來越少人在使用了 不是主流

ThreadPool-執行緒集區(大陸:線程池)

2.0 線程池 - 實作方式:享元模式 --數據庫連接池
1.thread提共太多的API,給三歲小孩一把槍
2.無限使用線程,ThreadPool加以限制
3.重用線程,避免重複的創建與銷毀

享元模式

執行方式

  ThreadPool.QueueUserWorkItem(t => this.doSomething("button4_Click"));

取得環境中最大可以使用的執行緒數量

public static void GetMaxThreads (out int workerThreads, out int completionPortThreads);

參數
workerThreads
Int32
執行緒集區中的背景工作執行緒最大數目。
completionPortThreads
Int32
執行緒集區中的非同步 I/O 執行緒最大數目。

 ThreadPool.GetAvailableThreads(out int workerThreads, 
            out int portThreads);

設定線程池最大數量跟最小數量

   ThreadPool.SetMaxThreads(16, 16);
   ThreadPool.SetMinThreads(8, 8);

執行緒等待

ThreadPool啥都沒有 所以要做等待可以使用以下方式

ManualResetEvent

ManualResetEvent 包含了一個bool屬性
false -- waitOne等待-- set true --WaitOne 才會往下執行
true --WaitOne會直接往下執行 --reset --false WaitOne 等待

ManualResetEvent manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(t =>
{
    this.doSomething("btnThreadPool");
    manualResetEvent.Set();
    //manualResetEvent.Reset();
});
manualResetEvent.WaitOne();

Console.WriteLine("等待QueueUserWorkItem完成後才執行");

一般來說,不要阻礙執行緒的執行,很容易不小心造成死鎖

Thread Callback範例

無返回值的非同步Callback

            var fun = this.ThreadWidthReturn<int>(() =>
            {
                Console.WriteLine($"fun 執行緒:{Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(2000);
                return DateTime.Now.Millisecond;
            });
            Console.WriteLine("123");

            int iResult = fun.Invoke();
            Console.WriteLine($"{iResult}");
        private void ThreadWithCallback(Action act, Action callback)
        {
            Thread thread = new Thread(() =>
              {
                  act.Invoke();
                  callback.Invoke();
              });
            thread.Start();
        }

有返回值的非同步Callback

要結果,不阻塞

            var fun = this.ThreadPoolWidthReturn<int>(() =>
            {
                Console.WriteLine($"fun 執行緒:{Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(2000);
                return DateTime.Now.Millisecond;
            });
            Console.WriteLine("123");

            int iResult = fun.Invoke();
            Console.WriteLine($"{iResult}");
 private Func<T> ThreadWidthReturn<T>(Func<T> func)
        {
            T t = default(T);
            Thread thread = new Thread(() =>
            {
                t = func.Invoke();
            });
            Console.WriteLine($"ThreadWidthReturn 執行緒:{Thread.CurrentThread.ManagedThreadId}");
            thread.Start();

            return () =>
            {
                thread.Join();
                Console.WriteLine($"return 執行緒:{Thread.CurrentThread.ManagedThreadId}");
                return t;
            };
        }

參考資料

C# 開發實戰:非同步程式開發技巧

本篇已同步發表至個人部落格
https://moushih.com/2022ithome19/


上一篇
玩轉C#之【執行序-計算機架構相關名詞】
下一篇
玩轉C#之【執行序-執行緒安全】
系列文
玩轉C# 進階學習之旅31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言