C# 各種多線程的實現方法簡易記錄


隨着.net版本不斷升級,目前多種多線程實現方法

一 .Thread    最基本

1.優缺點

優點--Thread API豐富

缺點--   1.線程資源是操作系統管理的,對API響應並不靈敏,(也就是調用一次提供的API可能不會立即響應)難以控制
    2.線程啟動數量是沒有控制的,可能會導致死機等意外發生

2.Thread對象實例化方法(四種)

  2.1聲明一個無參的、返回值為void的委托ThreadStart,委托內含一個靜態方法;

  2.2 聲明一個無參的、返回值為void的委托ThreadStart,委托內含一個對象方法;

  2.3 直接使用匿名委托;

  2.4 直接使用Lambda表達式;

 

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"主線程,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Thread thread1 = new Thread(new ThreadStart(NewThreadDisplay));//第一種

            AnotherObject another = new AnotherObject();
            Thread thread2 = new Thread(new ThreadStart(another.AnotherThreadDisplay));//第二種

            Thread thread3 = new Thread(delegate() { Console.WriteLine($"子線程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}"); } );//第三種
            Thread thread4 = new Thread(()=> Console.WriteLine($"子線程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}"));//第四種

            thread1.Start();//啟動子線程1
            thread2.Start();//啟動子線程2
            thread3.Start();//啟動子線程3
            thread4.Start();//啟動子線程4
            Console.ReadKey();
        }

    static void NewThreadDisplay()
        {
            Console.WriteLine($"子線程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
        }
    }
    class AnotherObject
    {
        public void AnotherThreadDisplay()
        {
            Console.WriteLine($"子線程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
        }
    }
}

輸出結果:

主線程,ThreadId: 1
子線程3,ThreadId: 5
子線程4,ThreadId: 6
子線程1,ThreadId: 3
子線程2,ThreadId: 4

 可以看出多線程啟用時無序的,即使一個線程在是先start的,執行也可能在后start的線程后面;並且每次執行的先后順序都不一定相同

3.關於線程優先級 Priority屬性
Priority屬性是一個ThreadPriority型枚舉,列舉了5個優先等級:Highest、AboveNormal、Normal(普通線程默認)、BelowNormal、Lowest

4.關於線程的狀態
通過ThreadState可以檢測線程是處於Unstarted、Sleeping、Running 等等狀態,它比 IsAlive 屬性能提供更多的特定信息。
通過CurrentContext可以獲取線程當前的上下文。
CurrentThread是最常用的一個屬性,它是用於獲取當前運行的線程。

5.列舉一些API(可能有坑)
終止-Abort() GetDomain()-返回當前線程正在其中運行的當前域 GetDomainId()   Interrupt()-中斷處於   WaitSleepJoin 線程狀態的線程

Join() - 已重載。 阻塞調用線程,直到某個線程終止時為止    Resume()-繼續運行已掛起的線程    Start()-執行本線程   Suspend()-掛起當前線程   

Sleep()- 把正在運行的線程掛起一段時間

6.關於前台線程和后台線程

前台線程:只有所有的前台線程都結束,應用程序才能結束。默認情況下創建的線程都是前台線程
后台線程:只要所有的前台線程結束,后台線程自動結束。通過Thread.IsBackground設置后台線程。必須在調用Start方法之前設置線程的類型,否則一旦線程運行,將無法改變其類型。
通過BeginXXX方法運行的線程都是后台線程。
注1:后台線程一般用於處理不重要的事情,應用程序結束時,后台線程是否執行完成對整個應用程序沒有影響。如果要執行的事情很重要,需要將線程設置為前台線程。

 

二. ThreadPool

.NetFramework 2.0 新增
基於池化資源管理設計思想,線程是一種資源,每次要用線程,就去申請一個線程,使用完釋放;池化就是一種容器,容器提前申請5個線程,程序需要使用線程直接找容器獲取,

用完再放回(空置狀態,避免頻繁申請和銷毀,容易還會根據限制的數量去申請和釋放)
優點:1線程復用 2限制最大線程數量
缺點:API太少了,在線程順序控制上弱,用起來不方便

 

三.Task--.NetFramework 3.0 新增

比較常用的多線程方式,全部是線程池線程、提供豐富API

Task線程全部是線程池線程、提供豐富API


Action action = o=>{xxxx;}
Task task =new Task(action);
task.Start();

舉例:

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        { 
            Console.WriteLine($"主線程開始,ThreadId: {Thread.CurrentThread.ManagedThreadId}");

            Task task1 = new Task(() => Console.WriteLine($"子線程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}"));
            task1.Start();

            //也可以直接創建並啟用
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Run(() => Console.WriteLine($"子線程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));
            taskList.Add(Task.Run(() => Console.WriteLine($"子線程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));
            taskList.Add(Task.Run(() => Console.WriteLine($"子線程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}")));

            Task.WaitAny(taskList.ToArray());////阻塞當前線程,直到任一任務結束
            Console.WriteLine($"有一個子線程執行完畢,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Task.WaitAll(taskList.ToArray());////阻塞當前線程,直到全部任務結束
            Console.WriteLine($"所有子線程執行完畢,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.WriteLine($"主線程結束,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.ReadKey();
        }

    }
}

執行結果:

主線程開始,ThreadId: 1
子線程1,ThreadId: 4
子線程2,ThreadId: 7
子線程3,ThreadId: 8
子線程4,ThreadId: 4
有一個子線程執行完畢,ThreadId: 1//阻塞了主線程執行!
所有子線程執行完畢,ThreadId: 1////阻塞了主線程執行!
主線程結束,ThreadId: 1

通過結果發現,無論怎么執行,最后三行輸出的順序都是一樣的!可見通過waitAny()、waitAll()方法可以阻塞主線程的執行,已經等達到一些順序控制的目的了!



注:1.盡量不要線程套線程,有更優秀的方法
2.子線程不能直接操作界面
等全部任務完成后啟動一個新的task完成后續動作
TaskFactory taskFactory =new TaskFactory();
taskFactory.ContinueWhenAll(taskList.ToArray(),tArray=>{
xxxx
})
等任一任務完成后啟動一個新的task完成后續動作
taskFactory.ContinueWhenAny(taskList.ToArray(),tArray=>{
xxxx
})
//continue的后續線程,不可能是主線程,其余皆可能

 

四.Parallel並行編程
主線程也參與計算,介於線程,節約一個線程
通過指定ParallelOptions控制最大並發數量
Parallel.Invoke(()=>{111;},()=>{222;},()=>{333;})

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        { 
            Console.WriteLine($"主線程開始,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Parallel.Invoke(new ParallelOptions() { MaxDegreeOfParallelism = 4},
                              () => {
                                  Console.WriteLine($"子線程1,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                Thread.Sleep(1000); },
                              () => {
                                  Console.WriteLine($"子線程2,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                Thread.Sleep(1000);},
                              () => {
                                  Console.WriteLine($"子線程3,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              },
                              () => {
                                  Console.WriteLine($"子線程4,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              },
                              () => {
                                  Console.WriteLine($"子線程5,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
                                  Thread.Sleep(1000);
                              });
            Console.WriteLine($"主線程結束,ThreadId: {Thread.CurrentThread.ManagedThreadId}");
            Console.ReadKey();
        }

    }
}

  

輸出結果:

主線程開始,ThreadId: 1
子線程1,ThreadId: 1
子線程2,ThreadId: 3
子線程3,ThreadId: 5
子線程4,ThreadId: 4
子線程5,ThreadId: 1
主線程結束,ThreadId: 1

特點:主線程(1)也參與計算,節約一個線程;可以通過ParallelOptions控制最大並發數量(本例設置為4),子線程5與線程1使用了同一個線程,所以5等1執行完了之后才會打印,同理主線程結束也在最后完成;

 

五、BeginInvoke()線程封裝相關的

委托.BeginInvoke()自動啟用異步多線程,並且帶 回調 BeginInvoke()

3.利用委托方式在子線程中改變主線程UI的內容

    private delegate void delegateUpdateLabel(string text);
    private void UpdateLabel(string text)
        {
            if (this.updateProcess_label.InvokeRequired)
            {
                Invoke(new delegateUpdateLabel(UpdateLabel), new object[] { text });
            }
            else
            {
                updateProcess_label.Text = text;
            }
        }
    IAsyncResult asyncResult1 = action.BeginInvoke("", null, null);    
    IAsyncResult asyncResult2 = func.BeginInvoke(null, null);

判斷異步線程完成方式:

1.利用asyncResult.IsCompleted 2.利用信號量asyncResult.AsyncWaitHandle.WaitOne(),可以做超時判斷 ---waitOne(-1)一直等待 waitOne(100)等待100ms
帶返回值的異步調用方法 returnValue = func.EndInvoke(asyncResult2) EndInvoke()也能用在回調里,但只能用一次

TODO...

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM