多線程之旅(Thread)

      在上篇文章中咱們已經知道了多線程是什麼了,那麼它到底能夠幹嗎呢?這裏特別聲明一個前面的委託沒看的同窗能夠到上上上篇博文查看,由於多線程要常用到委託源碼html

1、異步、同步git

      1.同步(在計算的理解老是要你措不及防,同步當線程作完一件事情以後,纔會執行後續動做),同步方法慢,只有一個線程執行,異步方法快,由於多個線程一塊兒幹活,可是二者並非線性增加,當咱們的異步線程佔有的資源愈來愈多了,會致使資源可能不夠,其次線程過多CPU也是須要管理成本的,因此不是越多越好。web

      2.異步(能夠同時執行多個任務,在一樣的時間,執行不一樣的任務),同步方法卡界面(UI),由於咱們的主線程(UI)忙於計算形成了堵塞了。異步方法不卡界面,計算任務交給了子線程完成。winform中體現的玲玲精緻。(你品,你細品),web 能夠異步的處理一塊兒其餘的任務,好比給用戶發郵箱(咱們的BS結構的,每次訪問都是一個子線程,當咱們的代碼寫的比較糟糕,是否是加載比較慢呢哈哈)。異步多線程無序,執行的前後無序,執行的時間不肯定,結束也不肯定,因此咱們很難經過執行時間和前後順序控制,異步的執行順序。安全

2、初識Thread多線程

屬性名稱 說明
CurrentContext 獲取線程正在其中執行的當前上下文。
CurrentThread 獲取當前正在運行的線程。
ExecutionContext 獲取一個 ExecutionContext 對象,該對象包含有關當前線程的各類上下文的信息。
IsAlive 獲取一個值,該值指示當前線程的執行狀態。
IsBackground 獲取或設置一個值,該值指示某個線程是否爲後臺線程。
IsThreadPoolThread 獲取一個值,該值指示線程是否屬於託管線程池。
ManagedThreadId 獲取當前託管線程的惟一標識符。
Name 獲取或設置線程的名稱。
Priority 獲取或設置一個值,該值指示線程的調度優先級。
ThreadState 獲取一個值,該值包含當前線程的狀態。

Thread 中包括了多個方法來控制線程的建立、掛起、中止、銷燬,後面的例子中會常用。異步

方法名稱 說明
Abort()     終止本線程。
GetDomain() 返回當前線程正在其中運行的當前域。
GetDomainId() 返回當前線程正在其中運行的當前域Id。
Interrupt() 中斷處於 WaitSleepJoin 線程狀態的線程。
Join() 已重載。 阻塞調用線程,直到某個線程終止時爲止。
Resume() 繼續運行已掛起的線程。
Start()   執行本線程。
Suspend() 掛起當前線程,若是當前線程已屬於掛起狀態則此不起做用
Sleep()   把正在運行的線程掛起一段時間。

      1.Thread是咱們.NET 1.0 給咱們提供的多線程類,能夠建立,和控制多線程,Thread類構造函數爲接受ThreadStart和ParameterizedThreadStart類型的委託參數,下面有請代碼神君。ide

        /// <summary>
        /// 使用Thread 建立多線程
        /// </summary>
        public static void Show()
        {
            //實例化建立線程 無參無返回值
            Thread thread = new Thread(() =>
            {
                Console.WriteLine("我是多線程");
            });
            thread.Start();

            //建立5個線程1
            for (int i = 0; i < 5; i++)
            {
                //這個之因此建立一個k,後面線程不安全會說到
                var k = i;
                //這是一個有參數無返回值多線程
                new Thread(x => Running(Convert.ToInt32(x))).Start(k);
            }
            Console.Read();
        }

 /// <summary>
        /// 一個執行須要長時間的任務
        /// </summary>
        static void Running(int s)
        {
            Console.WriteLine("**********************************");
            Console.WriteLine("執行開始啦" + s);
            Console.WriteLine("獲取當前執行的線程ID:" + Thread.CurrentThread.ManagedThreadId.ToString());
            var j = 0;
            for (int i = 0; i < 1000000000; i++)
            {
                j++;
            }
            Console.WriteLine("執行結束啦" + s);
        }
View Code

2、漸入佳境函數

  1.運行上面的代碼,能夠看到線程的無序性,雖然咱們的0最早開始執行的,可是不是第一個結束的,這個是由於咱們每一個線程執行的時間的不肯定性。這裏也要特別說明爲何Thread構造函數傳遞的是ThreadStart和ParameterizedThreadStart類型的委託參數,爲何不是Action ,Func,答案就是.NET 1.0的時候尚未Action 、Func。ThreadStart委託是一個無參無返回值上代碼中咱們建立了,ParameterizedThreadStart委託是一個有參數無返回值,可是咱們能夠看到咱們的參數是一個object類型,是一個不安全的參數(當時泛型也沒有出來)固然爲了防止這問題,咱們也是想到了方法,那就是咱們能夠經過一個泛型類,幫咱們限制參數類型。this

        /// <summary>
        /// 防止參數不安全
        /// </summary>
        public static void Show5()
        {
            //咱們建立一個泛型類,限制咱們的類型
            MyThread<string> mythread = new MyThread<string>("Thread_child");
            //將咱們的方法傳遞,進去
            Thread th3 = new Thread(mythread.ThreadChild);
            //啓動線程
            th3.Start();
        }

        /// <summary>
        /// 建立一個泛型類
        /// </summary>
        /// <typeparam name="T"></typeparam>
        class MyThread<T>
        {
            private T data;
            public MyThread(T data)
            {
                this.data = data;
            }
            public void ThreadChild()
            {
                Console.WriteLine("Child Thread Start! Result:{0}", data);
            }
        }
View Code

  2.咱們在上面還提供了其餘的方法,可是這些方法已經不建議使用了,如今已經棄用了,由於咱們沒法精確地控制線程的開啓與暫停,當咱們將線程掛起的時候,同時也會掛起線程使用的資源,會致使死鎖,不建議使用。將線程銷燬也不建議    不必定及時/有些動做發出收不回來。(這裏我使用的是.net Core 3.1 執行直接報錯了哈哈)spa

        /// <summary>
        /// 使用Thread 線程掛起、喚醒線程、銷燬,方式是拋異常、取消Abort異常
        /// </summary>
        public static void Show1()
        {
            //建立一個Thread 線程
            Thread thread = new Thread(() =>
            {
                Running();
            });
            //開啓線程
            thread.Start();
            //這個是線程掛起
            //thread.Suspend();
            //喚醒線程
            //thread.Resume();
            //上面的兩個方法,如今已經棄用了,由於咱們沒法精確地控制線程的開啓與暫停
            //當咱們將線程掛起的時候,同時也會掛起線程使用的資源,會致使死鎖,不建議使用
            try
            {
                //將線程銷燬
                //也不建議    不必定及時/有些動做發出收不回來
                thread.Abort();
            }
            catch (Exception)
            {
                //靜態方法將線程異常取消繼續工做
                Thread.ResetAbort();
            }
            Console.Read();
        }
View Code

   3.線程優先級,固然咱們的線程是一個無序的,也有控制線程執行的權重,可是這個優先級不是絕對的,由於線程的執行順序仍是看咱們的CPU爸爸的,可是咱們能夠利用Priority屬性作線程的權重執行,使用也很簡單

  /// <summary>
        /// 使用Thread 線程的優先級(可是執行仍是看CPU,能夠作優先級,可是不是絕對優先)
        /// </summary>
        public static void Show3()
        {
            //建立一個Thread 線程
            Thread thread = new Thread(() =>
            {
                Running();
            });
            thread.Start();
            //thread.Priority屬性能夠設置線程的優先級關係
            thread.Priority = ThreadPriority.Highest;
            Console.WriteLine("執行完啦啦啦啦啦啦啦啦啦啦啦拉拉");
            Console.Read();
        }
View Code

  4.前臺線程、後臺線程(這個字面意思,仍是和咱們的理解是不同的)咱們設置IsBackground控制線程是否(前/後)臺線程。默認是前臺線程,啓動以後必定要完成任務的,阻止進程退出。指定後臺線程:隨着進程退出。

 3、多線程起飛

  一、異步回調

    1.咱們的Thread沒有給我提供異步回調的功能,沒辦法須要本身造輪子了,咱們能夠先想一下回調的需求是什麼,需求分析:當咱們的線程任務執行完以後須要以後某些方法。咱們細品一下,咱們要執行完以後,在執行一我的任務,那就是同步執行異步方法了吧。咱們在子線程中怎麼同步執行呢?下面的代碼就實現了回調功能無論咱們執行多少次回調總會在任務後面執行。

/// <summary>
        /// 異步回調執行
        /// </summary>
        public static void Show6() {
            //建立一個任務委託
            ThreadStart threadStart = () => {
                Console.WriteLine("我是任務");
            };
            //建立一個回調執行的委託
            Action action = () => {
                Console.WriteLine("哈哈,我就是大家的回調方法哈,記得雙擊麼麼噠");
                Console.WriteLine("*********************************************");
            };
            ThreadWithCallback(threadStart, action);
            Console.ReadLine();
        }

/// <summary>
        /// 回調封裝 無返回值
        /// </summary>
        /// <param name="start"></param>
        /// <param name="callback">回調</param>
        private static void ThreadWithCallback(ThreadStart start, Action callback)
        {
            Thread thread = new Thread(() =>
            {
                start.Invoke();
                callback.Invoke();
            });
            thread.Start();
        }
View Code

   二、返回參數

    1.固然咱們使用線程須要返回參數,可是咱們的Thread沒有給咱們提供返回值的委託和方法,這個要莫子搞羅?固然咱們先分析需求,咱們要獲取返回值是否是要等線程執行以後呢?好的線程執行咱們可使用Join堵塞線程等它執行完畢,可是咱們要怎麼獲取返回值呢?對了咱們能夠建立一個變量,咱們的線程給變量賦值嗎?

/// <summary>
        /// 異步返回值
        /// </summary>
        public static void Show7()
        {
            //建立一個委託
            Func<string> func = () => {
                return "我是返回值";
            };
            //獲取執行結果
            Console.WriteLine(ThreadWithReturn(func).Invoke());
            Console.ReadLine();
        }

/// <summary>
        /// 有返回值封裝(請根據本案例自行封裝回調)
        /// </summary>
        /// <typeparam name="T">返回值類型</typeparam>
        /// <param name="func">須要子線程執行的方法</param>
        /// <returns></returns>
        private static Func<T> ThreadWithReturn<T>(Func<T> func)
        {
            //初始化一個泛型,限制咱們的類型
            T t = default(T);
            ThreadStart newStart = () =>
            {
                //線程給變量賦值
                t = func.Invoke();
            };
            //建立線程
            Thread thread = new Thread(newStart);
            //執行線程
            thread.Start();
            //建立一個委託 無參有返回值,執行委託會發生執行線程等待堵塞
            //當線程執行完以後,也就是說線程已經給變量t賦值了,咱們就返回t
            return new Func<T>(() =>
            {
                thread.Join();
                return t;
            });
        }
View Code

 4、Thread總結

  1.你們是否是以爲多線程很酷呢?哈哈我剛剛學的時候也是激動的心顫抖的手。固然文章中咱們介紹了不少API的使用,你們能夠動手試試,API的使用是小事,最重要的是咱們的思路,到咱們看到回調封裝和返回值封裝,咱們都是利用了多線程的一些特性,來完成的這些功能拓展的。咱們宏觀的看多線程感受很恐怖,可是在咱們作回調函數的時候是否是感受有一種微觀見解,線程執行的內部也是同步的執行哪些方法的。好了今天就寫到這裏昨天晚上9點多就睡了,早起擼個文章美滋滋。固然多線程還有講完的,才說道了.NET 1.0哈哈,後續的文章也會寫出來。

相關文章
相關標籤/搜索