多線程之旅(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
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...