欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#多線程基本使用小結(jié)

 更新時間:2024年10月16日 15:00:07   作者:小威編程  
C#多線程編程涉及Thread、Task、異步和Parallel等工具,Thread類用于創(chuàng)建獨立線程,通過Priority屬性設(shè)置優(yōu)先級,而線程池管理線程的調(diào)度和重用,本文給大家介紹C#多線程基本使用小結(jié),感興趣的朋友跟隨小編一起看看吧

線程是并發(fā)編程的基礎(chǔ)概念之一。在現(xiàn)代應(yīng)用程序中,我們通常需要執(zhí)行多個任務(wù)并行處理,以提高性能。C# 提供了多種并發(fā)編程工具,如Thread、Task、異步編程和Parallel等。

Thread 類

Thread 類是最基本的線程實現(xiàn)方法。使用Thread類,我們可以創(chuàng)建并管理獨立的線程來執(zhí)行任務(wù)。

基本使用Thread 

創(chuàng)建一個新的實例對象,將一個方法直接給Thread類,并使用實例對象啟動線程運行。

   static void Test() {
         Console.WriteLine("Test事件開始執(zhí)行");
            Thread.Sleep(1000);
            Console.WriteLine("Test事件睡眠之后打印數(shù)據(jù)");
            Console.WriteLine("Test事件id: " + Thread.CurrentThread.ManagedThreadId);
        }
        static void Main(string[] args)
        {
            #region //主線程id
            Console.WriteLine("主線程id: " + Thread.CurrentThread.ManagedThreadId);
            #endregion
            #region //傳遞一個方法給線程,執(zhí)行方法
            Thread t = new Thread(Test);
            t.Start();
            #endregion
}

線程數(shù)據(jù)傳輸

傳遞單個參數(shù)

Thread 提供了一個 Start(object parameter) 方法,可以在啟動線程時傳遞一個參數(shù)。

 
        static int Downlod(object obj) {
            string str = obj as string;
            Console.WriteLine(str);
        }
     static void Main(string[] args)
        {
      Thread t1 = new Thread(Downlod);
           //這里傳遞的參數(shù)是字符串
            t1.Start("數(shù)據(jù)傳遞方法執(zhí)行 ,數(shù)據(jù)傳遞方法id: "+Thread.CurrentThread.ManagedThreadId);
}

這種方式適用于需要傳遞單個參數(shù)的情況。

使用類的實例方法傳遞多個參數(shù)

先new一個類的實例,給實例字段賦值,調(diào)用類的實例,通過實例調(diào)用方法,構(gòu)造函數(shù)接收參數(shù),然后在線程中調(diào)用該實例的方法。

     static void Main(string[] args)
        {
// 使用 DownloadTool 實例化并傳遞數(shù)據(jù)
DownloadTool downloadTool = new DownloadTool("www.baidu.com", "這是下載鏈接哦");
Thread thread = new Thread(downloadTool.Download);
thread.Start();
}
public class DownloadTool
{
    private string url;
    private string message;
    public DownloadTool(string url, string message)
    {
        this.url = url;
        this.message = message;
    }
    public void Download()
    {
        Console.WriteLine("下載鏈接: " + url);
        Console.WriteLine("提示信息: " + message);
        Console.WriteLine("Download 線程 ID: " + Thread.CurrentThread.ManagedThreadId);
    }
}
 

thread 線程啟動時,它會執(zhí)行 downloadTool.Download() 方法,輸出傳遞的數(shù)據(jù)。

線程優(yōu)先級 

在 C# 中,可以使用 Thread.Priority 屬性來設(shè)置線程的優(yōu)先級。線程優(yōu)先級決定了操作系統(tǒng)在多線程環(huán)境中調(diào)度線程的順序,但并不保證高優(yōu)先級的線程總是比低優(yōu)先級的線程更早或更頻繁地執(zhí)行。

線程優(yōu)先級級別

C# 提供了五個線程優(yōu)先級級別,定義在 ThreadPriority 枚舉中:

  • Lowest:最低優(yōu)先級。操作系統(tǒng)盡可能少地調(diào)度這個優(yōu)先級的線程。
  • BelowNormal:低于正常的優(yōu)先級。優(yōu)先級比 Normal 低,但高于 Lowest。
  • Normal:默認優(yōu)先級,大多數(shù)線程默認的優(yōu)先級。適用于一般用途。
  • AboveNormal:高于正常的優(yōu)先級。操作系統(tǒng)更傾向于調(diào)度這個優(yōu)先級的線程。
  • Highest:最高優(yōu)先級。操作系統(tǒng)盡可能多地調(diào)度這個優(yōu)先級的線程。
  internal class Program
    {
        static void A()
        {
            int i = 0;
            while (true)
            {
                i++;
                Console.WriteLine($"A 輸出第{i}");
                Thread.Sleep(1000);
            }
        }
        static void B()
        {
            int i = 0;
            while (true)
            {
                i++;
                Console.WriteLine($"B 輸出第{i}");
                Thread.Sleep(1000);
            }
        }
        static void Main(string[] args)
        {
            //在C#中,線程的優(yōu)先級可以通過Thread.Priority屬性來設(shè)置和獲取。
//        Lowest: 線程的優(yōu)先級是最低的。在系統(tǒng)中存在其他活動線程時,此優(yōu)先級的線程很少得到執(zhí)行。
//BelowNormal: 線程的優(yōu)先級低于正常線程。
//Normal: 線程的優(yōu)先級是普通的,這是線程的默認優(yōu)先級。
//AboveNormal: 線程的優(yōu)先級高于正常線程。
//Highest: 線程的優(yōu)先級是最高的。此優(yōu)先級的線程會盡量優(yōu)先于其他所有優(yōu)先級的線程執(zhí)行。
            Thread a = new Thread(A);
            Thread b = new Thread(B);
            a.Priority = ThreadPriority.Highest;
            a.Start();
            b.Priority = ThreadPriority.Lowest;
            b.Start();
            Console.WriteLine("按任意鍵停止線程...");
            Console.ReadKey();
            a.Join();
            b.Join();
            Console.WriteLine("線程已停止");
        }
    }
  • A 被設(shè)置為最高優(yōu)先級 ThreadPriority.Highest。
  • B 被設(shè)置為最低優(yōu)先級 ThreadPriority.Lowest

注意事項

  • 優(yōu)先級不是絕對控制:操作系統(tǒng)可能會忽略優(yōu)先級設(shè)置,特別是在資源有限的系統(tǒng)中。高優(yōu)先級線程不一定會一直執(zhí)行,也不能阻止低優(yōu)先級線程的執(zhí)行。
  • 使用優(yōu)先級的適用場景:設(shè)置線程優(yōu)先級可能適用于實時系統(tǒng)(例如,某些任務(wù)需要優(yōu)先處理)。但是,大多數(shù)應(yīng)用程序通常可以使用默認的 Normal 優(yōu)先級。
  • 避免使用過多的高優(yōu)先級線程:如果所有線程都被設(shè)置為 Highest,系統(tǒng)的整體性能可能會下降,甚至導(dǎo)致線程爭用 CPU 資源的情況。
  • CPU 密集型任務(wù):在 CPU 密集型任務(wù)中,優(yōu)先級可能會對性能產(chǎn)生較大影響,因為優(yōu)先級高的線程可能會占用更多的 CPU 時間。

線程優(yōu)先級的最佳實踐

  • 默認使用 Normal 優(yōu)先級,除非有特殊原因。
  • 避免濫用 Highest 優(yōu)先級,因為它會對系統(tǒng)資源產(chǎn)生影響。
  • 在 I/O 密集型的線程中,優(yōu)先級通常不會有顯著差異,因為這些線程在等待 I/O 操作完成時,CPU 會調(diào)度其他線程。

通過合理設(shè)置線程優(yōu)先級,可以幫助操作系統(tǒng)更好地調(diào)度線程,以滿足應(yīng)用程序的需求。 但通常在 .NET 應(yīng)用程序中,多數(shù)情況下使用默認的 Normal 優(yōu)先級就足夠了。

線程池

線程池 (ThreadPool) 是一種高效的管理和調(diào)度線程的方式。線程池自動管理線程的創(chuàng)建、重用和銷毀,從而減少了手動創(chuàng)建和管理線程的開銷。

為什么使用線程池

  • 性能更高:線程池會重用現(xiàn)有的線程,減少了創(chuàng)建和銷毀線程的開銷。
  • 自動管理:線程池會根據(jù)系統(tǒng)負載動態(tài)調(diào)整線程數(shù)量。
  • 避免線程資源不足:線程池限制了同時運行的線程數(shù),避免了線程過多導(dǎo)致的資源耗盡問題。

基本使用 ThreadPool.QueueUserWorkItem 方法將任務(wù)排入線程池隊列。

using System;
using System.Threading;
class Program
{
    static void Main(string[] args)
    {
        // 將任務(wù)排入線程池
        ThreadPool.QueueUserWorkItem(DoWork, "任務(wù) 1");
        ThreadPool.QueueUserWorkItem(DoWork, "任務(wù) 2");
        Console.WriteLine("主線程完成");
        Thread.Sleep(3000); // 等待線程池中的任務(wù)完成
    }
    static void DoWork(object state)
    {
        string taskName = (string)state;
        Console.WriteLine($"{taskName} 開始執(zhí)行 - 線程ID: {Thread.CurrentThread.ManagedThreadId}");
        Thread.Sleep(1000); // 模擬耗時操作
        Console.WriteLine($"{taskName} 執(zhí)行完成 - 線程ID: {Thread.CurrentThread.ManagedThreadId}");
    }
}

運行結(jié)果QueueUserWorkItem自動分配線程來執(zhí)行任務(wù)。

QueueUserWorkItem 方法將任務(wù)排入線程池。它接收一個委托(即方法)和一個可選的狀態(tài)對象(傳遞給方法的數(shù)據(jù))。

DoWork 方法接受一個參數(shù) state,這是從 QueueUserWorkItem 傳遞的。

Thread.Sleep(3000) 確保主線程不會立即退出,使得線程池中的任務(wù)有機會完成。

使用帶返回值的線程池任務(wù)

C# 中的 ThreadPool 通常不直接支持返回值。如果需要獲得任務(wù)結(jié)果,可以使用 Task,因為 Task 本質(zhì)上也是線程池的一部分。Task 更適合于帶返回值的異步操作。這里使用 Task.Run 來代替 ThreadPool

     static void Main(string[] args)
        {
  //使用tesk多線程
           int a= Task.Run(() =>
            {
              int a =  Dowload();
                return a;
            }).Result;
            Task<int> task = Task<int>.Run(()=>{
             int a =   Dowload();
                return a;
            });
            //初始化一個CancellationTokenSource實例
            CancellationTokenSource source = new CancellationTokenSource();
            //task.Start();
            task.Wait(1000);
            source.Cancel();
        int result  =    task.Result;
            Console.WriteLine(result);
            Console.WriteLine($"tesk返回值{a}");
}
        static int  Dowload() {
            int a = 0;
            for (int i = 0; i < 10; i++)
            {
              a=  a + i + 1;
            }
         int?  id= Task.CurrentId;
            Console.WriteLine("Current thread ID: " + id);
            return a;
        }

線程池的限制

  • 任務(wù)運行時間過長:線程池中的線程本質(zhì)上是共享資源,如果某個任務(wù)運行時間太長,將會占用線程池中的線程,導(dǎo)致其他任務(wù)無法及時執(zhí)行。
  • 不適合實時系統(tǒng):線程池中的任務(wù)調(diào)度是由系統(tǒng)管理的,無法保證精確的實時性。
  • 有限的線程數(shù)量:在高并發(fā)場景中,如果線程池中的線程全部被占用,新的任務(wù)將會等待,直到有線程可用。

線程池總結(jié)

線程池是一種高效的并發(fā)處理方式,適合于大多數(shù)輕量級的后臺任務(wù)。在現(xiàn)代 C# 編程中,建議使用 Taskasync/await 進行異步操作,因為它們能簡化代碼,并且使用底層的線程池來管理線程。如果需要精確控制線程的執(zhí)行,通常建議使用手動管理的 Thread 等。 

線程鎖

使用多線程,在多線程編程中,如果多個線程同時訪問和修改共享資源(如全局變量、文件、數(shù)據(jù)庫等),可能會導(dǎo)致數(shù)據(jù)不一致或競爭條件。為了避免這種情況,多線程鎖通過控制對共享資源的訪問來保證線程安全性。

資源沖突示例:

  static void Main(string[] args)
        {
             //調(diào)用方法循環(huán)創(chuàng)建新的線程來執(zhí)行方法
            StateObject state = new StateObject();
            for (int i = 0; i < 30; i++)
            {
                Thread thread = new Thread(state.ChangState);
                thread.Start();
            }
            Console.ReadKey();
        }
//一個StateObject類
        public class StateObject
        {
            private int state = 5;
//里面有個狀態(tài)改變方法,當狀態(tài)等于5的時候進入到方法中,然后state+1 打印的應(yīng)該是6 和線程id
            public void ChangState()
            {
                if (state == 5)
                {
                    state++;
                    Console.WriteLine($"state:{state}  線程id:" + Thread.CurrentThread.ManagedThreadId);
                }
                state = 5;
            }
        }

 運行結(jié)果:

因為資源沖突的原因,有一些線程執(zhí)行的時候,可能另外一個線程沒有執(zhí)行完,另外一個線程就已經(jīng)進入到方法里面了。因為有修改的操作導(dǎo)致State狀態(tài)混亂,資源沖突。這時候我們就需要一個東西來維護,讓線程執(zhí)行指定方法時一個一個的執(zhí)行,不會沖突。

簡單使用lock鎖

1.創(chuàng)建一個private readonly object lockObject = new object(); // 鎖對象

2.使用lock (lockObject){

//業(yè)務(wù)代碼,執(zhí)行到有修改的操作的代碼

} // 使用鎖對象來確保線程安全

        static void Main(string[] args)
        {
            StateObject state = new StateObject();
            for (int i = 0; i < 100; i++)
            {
                Thread thread = new Thread(state.ChangState);
                thread.Start();
            }
            Console.ReadKey();
        }
        public class StateObject
        {
            private object _lock = new object();
            private int state = 5;
            public void ChangState()
            {
                //使用鎖保證每次執(zhí)行方法的都是一個線程,防止資源沖突
                lock (_lock)
                {
                    if (state == 5)
                    {
                        state++;
                        Console.WriteLine($"state:{state}  線程id:" + Thread.CurrentThread.ManagedThreadId);
                    }
                    state = 5;
                }
            }
        }

 這樣運行起來就能把每個線程操作隔離開,保證state的狀態(tài)不會沖突。

為什么要創(chuàng)建一個 object 對象作為鎖?

  • 專用性:創(chuàng)建一個專用的鎖對象(如 private readonly object lockObject = new object();),可以確保鎖定的是特定的同步邏輯,而不是其他對象。這有助于避免意外的鎖沖突或死鎖。
  • 避免使用其他共享對象:雖然可以使用任意的引用類型對象作為鎖對象(包括 thisType 對象),但這可能會帶來不必要的風(fēng)險,尤其是在 public 方法或?qū)ο笾?,這樣可能會導(dǎo)致意外的鎖定沖突。

常見問題死鎖:

鎖并不是萬能,也不是有鎖就是最好,要看情況使用,鎖也會產(chǎn)生問題,常見的問題就是死鎖等問題。

演示死鎖:

thread1方法1 的鎖里面嵌套鎖住了thread2的鎖,thread2方法2的鎖里面嵌套鎖住了thread1的鎖,這種鎖與鎖嵌套使用,就是容易出問題。導(dǎo)致線程鎖死程序無法動彈。

   static void Main(string[] args)
        {
            DeadlockExample deadlockExample = new DeadlockExample();
            Thread t1 = new Thread(deadlockExample.Thread1);
            Thread t2 = new Thread(deadlockExample.Thread2);
            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
            Console.ReadKey();
        }
    }
   public  class DeadlockExample
    {
        private static object lock1 = new object();
        private static object lock2 = new object();
        public  void Thread1()
        {
            lock (lock1)
            {
                Console.WriteLine("線程1:已獲取鎖1,正在等待鎖2。。。");
                Thread.Sleep(100);  // 模擬某些工作
                lock (lock2)
                {
                    Console.WriteLine("線程1:獲得鎖2");
                }
            }
        }
        public  void Thread2()
        {
            lock (lock2)
            {
                Console.WriteLine("線程2:已獲取鎖2,正在等待鎖1。。。");
                Thread.Sleep(100);  // 模擬某些工作
                lock (lock1)
                {
                    Console.WriteLine("線程2:獲得鎖1");
                }
            }
        }
    }

運行結(jié)果:

死鎖發(fā)生在兩個或多個線程相互等待對方持有的資源,導(dǎo)致所有線程都無法繼續(xù)執(zhí)行。

總結(jié):

多線程鎖在 C# 中主要用于解決以下問題:

  • 競態(tài)條件:通過鎖機制防止多個線程同時訪問和修改共享資源,確保數(shù)據(jù)一致性。
  • 死鎖:防止多個線程相互等待資源,通過鎖的順序或者避免嵌套鎖來解決。
  • 資源饑餓:確保每個線程都能獲取資源,使用 Monitor.TryEnter 等機制防止無限等待。
  • 讀寫鎖:允許多個線程并發(fā)讀取資源,但寫入時互斥,適合讀多寫少的場景。

C# 提供了多種鎖機制,開發(fā)者可以根據(jù)應(yīng)用場景選擇合適的鎖類型。

如果不想使用 lock 關(guān)鍵字,C# 還提供了其他鎖機制,比如 Mutex、Semaphore、Monitor

到此這篇關(guān)于C#多線程基本使用和探討的文章就介紹到這了,更多相關(guān)C#多線程使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#圖表算法之無向圖

    C#圖表算法之無向圖

    這篇文章介紹了C#圖表算法之無向圖,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • 用序列化實現(xiàn)List<T> 實例的深復(fù)制(推薦)

    用序列化實現(xiàn)List<T> 實例的深復(fù)制(推薦)

    下面小編就為大家?guī)硪黄眯蛄谢瘜崿F(xiàn)List<T> 實例的深復(fù)制(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • c#中利用委托反射將DataTable轉(zhuǎn)換為實體集的代碼

    c#中利用委托反射將DataTable轉(zhuǎn)換為實體集的代碼

    c#中利用委托反射將DataTable轉(zhuǎn)換為實體集的代碼,需要的朋友可以參考下
    2012-10-10
  • c#的datatable轉(zhuǎn)list示例

    c#的datatable轉(zhuǎn)list示例

    這篇文章主要介紹了c#的datatable轉(zhuǎn)list示例,代碼中有注釋,需要的朋友可以參考下
    2014-04-04
  • 不用IDE寫C#的Hello World的方法

    不用IDE寫C#的Hello World的方法

    這篇文章主要介紹了不用IDE寫C#的Hello World的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2015-10-10
  • 使用C#在注冊表中保存信息的例子

    使用C#在注冊表中保存信息的例子

    最近做的項目需要在注冊表中記錄一些用戶設(shè)置,方便在程序下次啟動時讀取設(shè)置,應(yīng)用上次用戶保存的設(shè)置,挺簡單的
    2014-04-04
  • WPF實現(xiàn)列表分頁控件的示例代碼

    WPF實現(xiàn)列表分頁控件的示例代碼

    這篇文章主要為大家詳細介紹了如何利用WPF實現(xiàn)列表分頁控件,文中的示例代碼講解詳細,對我們學(xué)習(xí)或工作有一定幫助,感興趣的小伙伴可以了解一下
    2022-10-10
  • 深入理解C#中常見的委托

    深入理解C#中常見的委托

    本篇文章是對C#中常見的委托進行了詳細的分析介紹,需要的朋友參考下
    2013-06-06
  • 淺析C#中結(jié)構(gòu)與類的區(qū)別

    淺析C#中結(jié)構(gòu)與類的區(qū)別

    本文主要對C#結(jié)構(gòu)與類的區(qū)別進行簡要分析,文中舉了實例,便于理解,具有很好的參考價值,需要的朋友一起來看下吧
    2016-12-12
  • Unity搖桿制作的方法

    Unity搖桿制作的方法

    這篇文章主要為大家詳細介紹了Unity搖桿制作的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08

最新評論