1. 程式人生 > >【C#多執行緒】1.Thread類的使用及注意要點

【C#多執行緒】1.Thread類的使用及注意要點

Thread隨便講講

  因為在C#中,Thread類在我們的新業務上並不常用了(因為建立一個新執行緒要比直接從執行緒池拿執行緒更加耗費資源),並且在.NET4.0後新增了Task類即Async與await關鍵字,使得我們基本不再用Thread了,不過在學習多執行緒前,有必要先了解下Thread類,這裡就先隨便講講Thread。

1.使用多執行緒的幾種方式

  多執行緒Thread類只支援執行兩種方法,一種是無引數並且無返回值的方法,第二種是有一個Object型別引數(有且只能有一個引數,並且必須是Object型別)且無返回值的方法。如果想讓多執行緒方法攜帶多個引數,可以將多個引數放入一個集合或陣列中傳入方法。

  下面例子使用了控制檯來演示多執行緒的簡單使用:

using System;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        //無引數無返回值方法
        public static void DoSomething()
        {
            for (int i = 0; i < 100; i++)
            {
                Thread.Sleep(500);
            }
        }
        //有引數無返回值方法
        public static void DoSomethingWithParameter(object obj)
        {
            for (int i = 0; i < (int)obj; i++)
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
            }
        }

   
        static void Main(string[] args)
        {
            //獲取主執行緒ID
            int currentThreadId = Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine($"---------主執行緒<{currentThreadId}>開始執行---------");
            //多執行緒執行無引數方法方式1
            ThreadStart ts = DoSomething;//ThreadStart是一個無引數,無返回值的委託
            Thread thread1 = new Thread(ts);
            thread1.Start();
            
            //多執行緒執行無引數方法方式2
            Thread thread2 = new Thread(DoSomething);//可省略ThreadStart
            thread2.Start();

            //多執行緒執行有引數方法方式1
            //ParameterizedThreadStart是一個有一個Object型別引數,但是無返回值的委託。
            ParameterizedThreadStart pts = DoSomethingWithParameter;
            Thread thread3 = new Thread(pts);
            thread3.Start(100);
            
            //多執行緒執行有引數方法方式2
            //可以省略ParameterizedThreadStart
            Thread thread4 = new Thread(DoSomethingWithParameter);
            thread4.Start(100);

            //還可以使用lamda表示式簡化多執行緒寫法
            new Thread(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(500);
                }
            }).Start();
            Console.WriteLine($"---------主執行緒<{currentThreadId}>執行結束---------");
        }
    }
}

  執行結果如下:

  

 

 

 

 2.前臺執行緒與後臺執行緒

  • 前臺執行緒

  如主執行緒(或稱為UI執行緒)就是前臺執行緒,預設Thread的例項均為前臺執行緒,前臺執行緒的特點是,如果當前應用的前臺執行緒沒有全部執行完畢,那麼當前應用就無法退出。舉個例子,我們知道正常情況下,控制檯應用在Main方法結束後會自動結束當前程序,如果我們在Main方法中建立了一個新Thread執行緒,並使其保持執行,那麼即使Main方法執行完畢,控制檯程序也無法自動關閉(除非手動右上角點×)。就如下圖情況,畫紅圈的地方表示Main方法執行完畢,可是程式依舊在執行,所以我們一般在用Thread的時候會將Thread設定為後臺執行緒。

 

 

 

  • 後臺執行緒

  後臺執行緒與前臺執行緒的唯一區別是,它不會去影響程式的生老病死,當程式的前臺執行緒全部關閉(即程式退出),那麼即使程式的後臺執行緒依舊在執行任務,那麼也會強制關閉。

  設定Thread為後臺執行緒的方式:

        Thread tt = new Thread(DoSomething);
        tt.IsBackground = true;//設定tt為後臺執行緒
        tt.Start();

  前臺執行緒與後臺執行緒對程式的影響效果看似好像不算大,但是如果我們在做Winform或者WPF專案時,若在某窗體內執行一個新執行緒任務(這個新執行緒是前臺執行緒),如果在任務執行期間關閉程式,此時會發現,雖然介面都被關閉,但是計算機工作管理員中此程式依舊還在執行(並且如果在新執行緒中執行的任務異常導致執行緒無法關閉,那麼這個程式就會一直在後臺跑下去),再次開啟程式可能會導致打不開等後果,這種行為是非常不好的。所以我們一般使用多執行緒Thread類時,最好順手將它設定為後臺執行緒。我們可以舉個例子。

        static void Main(string[] args)
        {
            //獲取主執行緒ID
            int currentThreadId = Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine($"---------主執行緒<{currentThreadId}>開始執行---------");

            //執行一個大概可以執行50秒的新執行緒
            Thread t = new Thread(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(500);
                }
            });
            t.IsBackground = true;//設定t為後臺執行緒
            t.Start();

            Console.WriteLine($"---------主執行緒<{currentThreadId}>執行結束---------");
        }

  這個例子的執行結果就不截圖了,因為控制檯會一閃而過(立即執行完Main方法便關閉),即使後臺執行緒t還在執行任務,但是也會強制關閉。

 

3.讓主執行緒等待新執行緒執行完成後再繼續執行(使用Thread的Join方法)

  直接上程式碼:

        static void Main(string[] args)
        {
            //獲取主執行緒ID
            int currentThreadId = Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine($"---------主執行緒<{currentThreadId}>開始執行---------");

            //執行一個大概可以執行50秒的新執行緒
            Thread t = new Thread(() =>
            {
                for (int i = 0; i < 20; i++)
                {
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(500);
                }
            });
            t.IsBackground = true;//設定t為後臺執行緒
            t.Start();

            t.Join();//在t執行緒執行期間,如果主執行緒呼叫t執行緒的Join方法,主執行緒會卡在這個地方直到t執行緒執行完畢

            Console.WriteLine($"---------主執行緒<{currentThreadId}>執行結束---------");
        }

 

4.Thread例項的其他常用方法

  直接看程式碼註釋吧:

        static void Main(string[] args)
        {
            //執行一個大概可以執行50秒的新執行緒
            Thread t = new Thread(DoSth);
            t.IsBackground = true;//設定t為後臺執行緒
            t.Start();

            t.Join();//在t執行緒執行期間,如果主執行緒呼叫t執行緒的Join方法,主執行緒會卡在這個地方知道t執行緒執行完畢
            t.Priority = ThreadPriority.Normal;//設定執行緒排程的優先順序
            ThreadState rhreadState = t.ThreadState;//獲取執行緒執行狀態。
            bool b = t.IsAlive;//獲取執行緒當前是否存活
            t.Interrupt();//中斷當前執行緒
            t.Abort();//終止執行緒          
        }

 

5.Thread類的常用方法

  直接看程式碼註釋吧:

        static void Main(string[] args)
        {
            //使得當前執行緒暫停1秒再繼續執行,此處會暫停主執行緒1秒鐘
            //如果寫在其他執行緒執行的方法中,會讓執行那個方法的執行緒暫停1秒再繼續執行)
            Thread.Sleep(1000);

            //獲取當前執行執行緒的執行緒例項
            Thread t = Thread.CurrentThread;             
        }

  

  

  下節我們會簡單講講執行緒池+同步回撥+非同步回撥+跨執行緒訪問U