多線程之旅(Thread)

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

在上篇文章中我們已經知道了多線程是什麼了,那麼它到底可以幹嘛呢?這裡特別聲明一個前面的委托沒看的同學可以到上上上篇博文查看,因為多線程要經常使用到委托。源碼 一、非同步、同步 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哈哈,後續的文章也會寫出來。


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

-Advertisement-
Play Games
更多相關文章
  • 在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 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...