多線程之旅(Thread)

来源:https://www.cnblogs.com/chenxi001/archive/2020/03/29/12590724.html

在上篇文章中我們已經知道了多線程是什麼了,那麼它到底可以幹嘛呢?這裡特別聲明一個前面的委托沒看的同學可以到上上上篇博文查看,因為多線程要經常使用到委托。源碼 一、非同步、同步 1.同步(在計算的理解總是要你措不及防,同步當線程做完一件事情之後,才會執行後續動作),同步方法慢,只有一個線程執行,非同步方法 ...


      在上篇文章中我們已經知道了多線程是什麼了,那麼它到底可以幹嘛呢?這裡特別聲明一個前面的委托沒看的同學可以到上上上篇博文查看,因為多線程要經常使用到委托源碼

一、非同步、同步

      1.同步(在計算的理解總是要你措不及防,同步當線程做完一件事情之後,才會執行後續動作),同步方法慢,只有一個線程執行,非同步方法快,因為多個線程一起幹活,但是兩者並不是線性增長,當我們的非同步線程占有的資源越來越多了,會導致資源可能不夠,其次線程過多CPU也是需要管理成本的,所以不是越多越好。

      2.非同步(可以同時執行多個任務,在同樣的時間,執行不同的任務),同步方法卡界面(UI),因為我們的主線程(UI)忙於計算造成了堵塞了。非同步方法不卡界面,計算任務交給了子線程完成。winform中體現的玲玲精緻。(你品,你細品),web 可以非同步的處理一起其他的任務,比如給用戶發郵箱(我們的BS結構的,每次訪問都是一個子線程,當我們的代碼寫的比較糟糕,是不是載入比較慢呢哈哈)。非同步多線程無序,執行的先後無序,執行的時間不確定,結束也不確定,所以我們很難通過執行時間和先後順序控制,非同步的執行順序。

二、初識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類型的委托參數,下麵有請代碼神君。

        /// <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

二、漸入佳境

  1.運行上面的代碼,可以看到線程的無序性,雖然我們的0最先開始執行的,但是不是第一個結束的,這個是因為我們每個線程執行的時間的不確定性。這裡也要特別說明為什麼Thread構造函數傳遞的是ThreadStart和ParameterizedThreadStart類型的委托參數,為什麼不是Action ,Func,答案就是.NET 1.0的時候還沒有Action 、Func。ThreadStart委托是一個無參無返回值上代碼中我們創建了,ParameterizedThreadStart委托是一個有參數無返回值,但是我們可以看到我們的參數是一個object類型,是一個不安全的參數(當時泛型也沒有出來)當然為了防止這問題,我們也是想到了方法,那就是我們可以通過一個泛型類,幫我們限制參數類型。

        /// <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 執行直接報錯了哈哈)

        /// <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控制線程是否(前/後)台線程。預設是前臺線程,啟動之後一定要完成任務的,阻止進程退出。指定後臺線程:隨著進程退出。

 三、多線程起飛

  1、非同步回調

    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

   2、返回參數

    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

 四、Thread總結

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


您的分享是我們最大的動力!

更多相關文章
  • 在ASP.NET MVC中有四種過濾器類型 Action 1、在ASP.NET MVC項目中,新建文件夾Filter,然後新建類MyCustormFilter,繼承自ActionFilterAttribute類,我們來看下ActionFilterAttribute類有如下四個方法,從命名我應該就可以 ...
  • 面向對象 面向對象是一個抽象的概念,其本質就是對事物以抽象的方式建立對應的模型。 簡單來講,比如我有一隻鋼筆,那麼我就可以通過分析,可以得到 這隻鋼筆的材第是塑料,品牌是個雜牌 ,裡面裝的墨是黑色的,可以用。這時候就能建立一個鋼筆的模型,它在這裡應該有這些屬性: 圖是一個不正確的UML類圖,但是可以 ...
  • 在上一篇abp(net core)+easyui+efcore實現倉儲管理系統——入庫管理之五(四十一) 文章中實現了入庫管理的列表頁面,並實現了控制器的代碼。在今天我們學習如何在前端實現新增入庫單信息界面。 ...
  • 在偶然一次調試某程式時,遇到提示: 無法載入程式集*****.XmlSerializers.dll,文件找不到(Could not load file or assembly ****.XmlSerializers.dll , FileNotFoundException...)。於是嘗試在項目屬性中 ...
  • 首先,好消息是Goole將於2020年2月份發佈Chrome 80版本。本次發佈將推進Google的“漸進改良Cookie”策略,打造一個更為安全和保障用戶隱私的網路環境。 壞消息是,本次更新可能導致瀏覽器無法向服務端發送Cookie。如果你有多個不同功能變數名稱的應用,部分用戶很有可能出現會話時常被打斷的 ...
  • TerminalMACS(Terminal Manager And Check System) 遠程終端管理和檢測系統 本文同步更新地址:https://dotnet9.com/11429.html 一、本系統可監控多種終端資源: 移動端 Android iOS PC端 Windows Linux ...
  • 一、引言 RabbitMQ是Rabbit Message Queue的簡寫,但不能僅僅理解其為消息隊列,消息代理更合適。RabbitMQ是一個由 Erlang 語言開發的AMQP(高級消息隊列協議)的開源實現,其內部結構如下: RabbitMQ作為一個消息代理,主要和消息打交道,負責接收並轉發消息。 ...
  • 本文主要是講解stopwatch對程式運行時間的準確測量 僅僅介紹裡面的StartNew()方法,Restart()方法和ElapsedMilliseconds { get;}屬性 public void StartNew():作用是對新的 System.Diagnostics.Stopwatch ...
一周排行
  • 一:背景 1. 講故事 如果你常翻看FCL的源碼,你會發現這裡面有不少方法藉助了C/C++的力量讓C#更快更強悍,如下所示: [DllImport("QCall", CharSet = CharSet.Unicode)] [SecurityCritical] [SuppressUnmanagedCo ...
  • 上一篇(https://www.cnblogs.com/meowv/p/12966092.html)文章使用AutoMapper來處理對象與對象之間的映射關係,本篇主要圍繞定時任務和數據抓取相關的知識點並結合實際應用,在定時任務中迴圈處理爬蟲任務抓取數據。 開始之前可以刪掉之前測試用的幾個Hello ...
  • 首先創建實體類 1 public class MacState 2 { 3 /// <summary> 4 /// 請求狀態 5 /// </summary> 6 public string success { get; set; } 7 /// <summary> 8 /// 錯誤信息 9 /// ...
  • 0. 前言 前幾天FreeSql的作者向我推薦了FreeSql框架,想讓我幫忙寫個文章介紹一下。嗯,想不到我也能帶個貨了。哈哈,開個玩笑~看了下覺得設計的挺有意思的,所以就謝了這篇文章。 簡單介紹一下,FreeSql 是NCC組織的沙盒級項目,是一款功能強大的 ORM 組件,支持 .NET Core ...
  • 0. 前言 這是一個新的系列,名字是《ASP.NET Core 入門到實戰》。這個系列主講ASP.NET Core MVC,輔助一些前端的基礎知識(能用來實現我們需要的即可,並非主講)。同時這個系列也會在後續介紹ASP.NET Core 平臺的其它類型的項目,並帶領大家以各個類型的項目為主要架構開發 ...
  • 我寫了一個Winform測試程式,用的System.Timers.Timer,在事件里,設置label1.Text,然後,居然句柄泄漏、用戶對象泄漏! 百思不得其解,最後換成System.Windows.Forms.Timer,居然不泄漏了! 最近睡眠不足,哪怕一個很小的問題,隨便搞搞,都半夜了! ...
  • leetcode-7. 整數反轉。 給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。 示例 1: 輸入: 123 輸出: 321 示例 2: 輸入: -123 輸出: -321 示例 3: 輸入: 120 輸出: 21 註意: 假設環境只能存儲得下 32 位的有符號整數,則其 ...
  • 1. Java 虛擬機是什麼? 1.1 虛擬機 虛擬機:虛擬的電腦,一個用來執行虛擬電腦指令的軟體。 虛擬機分為系統虛擬機和程式虛擬機。 系統虛擬機:提供一個可運行完整操作系統的軟體平臺,如 Visual Box、VMware。 程式虛擬機:專門執行單個程式的,典型代表 Java 虛擬機。Jav ...
  • 前言 - strlen 概述 無意間掃到 glibc strlen.c 中代碼, 久久不能忘懷. 在一無所知的編程生涯中又記起點點滴滴: 編程可不是兒戲 ❀, 有些難, 也有些不捨. 隨軌跡一同重溫, 曾經最熟悉的 strlen 手感吧 ~ /* Copyright (C) 1991-2020 Fr ...
  • 背景 隊列[Queue]:是一種限定僅在表頭進行刪除操作,僅在表尾進行插入操作的線性表;即先進先出(FIFO-first in first out):最先插入的元素最先出來。 本文通過編碼實現鏈式隊列類,並模擬一個有趣的應用,能夠幫助我們對鏈式隊列有更深度的理解。 基本概念 結點 每個元素,除了存儲 ...