C# 多線程Thread和ThreadPool實現類似Task.Wait()效果的回調,線程順序控制

重點是總結一些常用用法含義,個人體會他和委託密切相關,尤其是開啓線程,初始化,
第一個知識點:Thread線程開啓,自定義方式解決線程執行完畢回調問題,達成Task.wait()的效果

//進一步如果要調用是帶參數的返回呢?
        //定義一個方法,基於傳遞一個委託參數,方法體裏面將這個委託執行的值,作爲參數,傳遞給開啓線程執行另外一個委託,
        //緊接着返回這個
        private static T CallReturn<T>(Func<T> func)
        {
            T t = default(T);
            //根據參數傳進來的委託,執行完返回的值,進行接收
            //開啓新線程,執行新委託
            //ThreadStart thread3 = new ThreadStart(()=>{
            //    t = func.Invoke();
            //});
            //Thread t3 = new Thread(thread3);
            //t3.Start(); 

            //一個多線程,像是異步的感覺
            Thread t3 = new Thread(() =>
            {
                t = func.Invoke();
            });
            t3.Start();
            return t;
        }

第二個知識點:線程池,利用額外的ManualResetEvent事件,達成Task.wait()的效果

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<int> func1 = () =>
            {
                return DateTime.Now.Year;
            };

            //Thread開啓線程,但並沒有提供控制線程執行順序的Api,如何做呢?
            //如果讓某個線程
            Thread thread1 = new Thread(() => {
                Thread.Sleep(2000);
                Console.WriteLine("我是線程1");
            });
            //thread1.Start();
            //如何讓線程1結束之後,才執行線程2
            //**********第一個方式:while()
            //while (thread1.ThreadState != ThreadState.Stopped)
            //{
            //    Thread.Sleep(100);
            //}
            //如果開啓線程沒有停止,阻塞當前主線程,即不執行線程2
            //這樣帶來一個問題,當前程序顯示無響應狀態,即卡頓狀態
            Thread thread2 = new Thread(() =>
            {
             //***********第二個方式:Join()
                //阻塞線程1,查看官方文檔即可知道它的作用
                //直到它執行完成,如果沒有執行完,將一直阻塞,裏面可以傳入一個時間參數
                thread1.Join();


                Console.WriteLine("我是線程2");
            });
            //thread2.Start();
            //明顯看出,即使線程1先開啓,但是執行時間長,仍然是後執行輸出,
            //僅僅是通過代碼先後順序,無法真正執行控制順序

            //********第三種方式,兩個委託,放在一個前臺線程中執行
            //如下需要兩個委託,順序執行,並且依賴第一個委託的結果,如下獲取年份,
            //調用方法,獲取結果應該還是0,爲啥,因爲線程開啓太慢,
            //方法中線程還沒來得及開啓
            //int res = CallReturn<int>(func1);
            Func<int> funres = CallReturnFunc<int>(func1);
            //這裏實際上是讓委託在子線程裏執行,

            int res = funres.Invoke();
            //這裏實際確保讓子線程內容執行完成
        }

       
        //進一步如果要調用是帶參數的返回呢?
        //定義一個方法,基於傳遞一個委託參數,方法體裏面將這個委託執行的值,作爲參數,傳遞給開啓線程執行另外一個委託,
        //緊接着返回這個
        private static T CallReturn<T>(Func<T> func)
        {
            T t = default(T);
            //根據參數傳進來的委託,執行完返回的值,進行接收
            //開啓新線程,執行新委託
            //ThreadStart thread3 = new ThreadStart(()=>{
            //    t = func.Invoke();
            //});
            //Thread t3 = new Thread(thread3);
            //t3.Start(); 

            //一個多線程,像是異步的感覺
            Thread t3 = new Thread(() =>
            {
                t = func.Invoke();
            });
            t3.Start();
            return t;
        }

        //線程開啓執行, 還沒來得及執行完,主線程已經執行完畢,值未修改,優化如下:
        //返回一個委託,裏面強制要求他執行完,藉助join方法,返回這個委託後,
        //調用方法返回值委託的invoke方法即可

        //方法後面定義的T,後面才能使用
        private static Func<T> CallReturnFunc<T>(Func<T> func1) {
            T t=default(T);
            Thread thread = new Thread(() => {
                t = func1.Invoke();
            });
            thread.Start();
            //確保子線程執行完,返回一個委託,裏面讓join執行,
            //同時在外面方法調用返回委託的Invoke方法
            
            //return func1;
            return new Func<T>(() => {
                thread.Join();
                return t;
            });
        }

        //線程功能強大,但是使用起來並不友好,有點難使用,
        //問題的關鍵在於線程數量是沒有控制的,在系統資源可用的情況下,可一直使用,
        //對於線程的開闢和銷燬都是由程序員來控制,並且這個過程代價比較大
        //正是由於這一個特性,提供一個新的方法,即線程池方式,
        //線程池的思想,事先創建一部分的線程,放在線程中待用,
        //要用直接取來用,用完還回來即可
    }
}

這種原生的Thread方式,功能強大,不過掌握起來不太容易,很容易是得不償失,原因是線程創建,關閉都是程序員操作,關鍵是平臺沒有辦法限制,考慮線程創建銷燬,極其佔用資源,就像有一句說的,一個幾歲小孩手持Ak步槍,使用不好,很有可能對自己造成傷害,另外一個思路應運而生,就是線程池,

簡而言之是,不再是創建線程,而是從線程池中申請,取來用,用了還

第二個知識點:線程池,利用額外的ManualResetEvent事件,達成Task.wait()的效果

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadPoolStart
{
    class Program
    {
        //https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.waitcallback?view=netcore-3.1
        /*WaitCallback 表示要在 ThreadPool 線程上執行的回調方法。 通過向 WaitCallback 構造函數傳遞迴調方法來創建委託。 你的方法必須具有此處顯示的簽名。
通過將 WaitCallback 委託傳遞給 ThreadPool.QueueUserWorkItem來將方法排隊以便執行。 當線程池線程變得可用時,回調方法會執行。*/
        static void Main(string[] args)
        {
            //如果希望讓線程池中線程等待,通過ManualResetEvent
            ManualResetEvent mre = new ManualResetEvent(false);
            //一直等待一個信號,有點監聽的意思,等待誰呢?
            //等待mre的ret,這個信號執行完成


            //開啓一個線程池,簡單方法是直接傳遞一個方法
            //默認需要符合WaitCallBack委託的方法,實際上傳值給這個委託類型,無參帶返回值
            //第一種方式,直接一個帶參無返回值的方法
            //這個方法,有點像是Invoke執行一個委託,
            //直接就是執行它
           bool b1= ThreadPool.QueueUserWorkItem(TestThreadPool,mre);
            //?????????傳一個數據參數,反而不執行
           //Thread.Sleep(1);
            //new一個WaitCallBack委託
            WaitCallback callback = new WaitCallback((o) => {
                Console.WriteLine("我是委託實例中調用方法,傳入數據:"+o.ToString());
                
            });
            //休眠1秒,仍然只有b2執行,因爲b1還未執行完,隨着主線程結束而結束
            bool b2 = ThreadPool.QueueUserWorkItem(callback,"2");
            //主線程休眠1ms,等待線程池中線程開始執行其中代碼
            //由於線程中線程都是後臺線程,主線程關閉相對應子線程都結束
            //Thread.Sleep(1000);

          
            mre.WaitOne();
        }
        private static void TestThreadPool(object o){
            ManualResetEvent mre=(ManualResetEvent)o;
           
            Thread.Sleep(2000);
            Console.WriteLine("線程中線程執行回調方法!方法參數:"+o.ToString());
            mre.Set();
            //這種情況,該如何傳遞這個信號
        }
    }
}

當然實際多線程使用,使用更加廣泛的是Task,下一篇再講,它使用起來更加方便快捷,不過它的實現原理,也是依賴於線程池,實際上C#語言,實現對線程的控制,主要就是這兩種方式Thread和ThreadPool,其他方式都是進一步封裝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章