程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 多線程(基礎篇2),多線程基礎篇2

多線程(基礎篇2),多線程基礎篇2

編輯:關於.NET

多線程(基礎篇2),多線程基礎篇2


  在上一篇多線程(基礎篇1)中,我們主要講述了如何創建線程、中止線程、線程等待以及終止線程的相關知識,在本篇中我們繼續講述有關線程的一些知識。

五、確定線程的狀態

   在這一節中,我們將講述如何查看一個線程的狀態,通常知道一個線程處於什麼狀態是非常有用的。但是,要注意線程是獨立運行的,它的狀態可能隨時變化。要查看一個線程的狀態,我們可以按如下步驟進行:

1、使用Visual Studio 2015創建一個新的控制台應用程序。

2、雙擊打開“Program.cs”文件,然後修改為如下代碼:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe05
 7 {
 8     class Program
 9     {
10         static void DoNothing()
11         {
12             Sleep(TimeSpan.FromSeconds(2));
13         }
14 
15         static void PrintNumbersWithStatus()
16         {
17             WriteLine("Starting...");
18             WriteLine(CurrentThread.ThreadState.ToString());
19 
20             for(int i = 1; i < 10; i++)
21             {
22                 Sleep(TimeSpan.FromSeconds(2));
23                 WriteLine(i);
24             }
25         }
26 
27         static void Main(string[] args)
28         {
29             WriteLine("Starting program...");
30 
31             Thread t1 = new Thread(PrintNumbersWithStatus);
32             Thread t2 = new Thread(DoNothing);
33 
34             WriteLine(t1.ThreadState.ToString());
35 
36             t2.Start();
37             t1.Start();
38 
39             for(int i = 1; i < 30; i++)
40             {
41                 WriteLine(i.ToString() + " - " + t1.ThreadState.ToString());
42             }
43 
44             Sleep(TimeSpan.FromSeconds(6));
45 
46             t1.Abort();
47 
48             WriteLine("A thread has been aborted");
49             WriteLine(t1.ThreadState.ToString());
50             WriteLine(t2.ThreadState.ToString());
51         }
52     }
53 }

3、運行該控制台應用程序,運行效果(每次運行效果可能不同)如下圖所示:

  在上述代碼中,我們在“Main”方法中定義了兩個不同的線程t1和t2,t1線程在執行過程中被終止,t2線程成功執行完畢。我們可以使用Thread對象的ThreadState屬性獲得某個線程的狀態信息,ThreadState屬性是C#枚舉類型。

  當程序執行到第34行代碼處時,t1線程還沒有執行,這個時候t1線程的狀態為“ThreadState.Unstarted”。

  當程序執行到第37行代碼處時,t1線程開始執行,這個時候該線程的狀態為“ThreadState.Running”。

  當運行到第39~42行代碼處時,主線程開始執行循環語句,持續打印出29條t1線程的狀態,因為在“PrintNumbersWithStatus”方法中調用了“Thread.Sleep”方法,因此在執行主線程的循環的過程中,t1線程的狀態在“ThreadState.Running”和“ThreadState.WaitSleepJoin”之間轉換。

  當程序執行到第44行代碼處時,在主線程中調用了“Thread.Sleep”方法,在等待6秒期間,“PrintNumbersWithStatus”方法中的for循環代碼不斷執行,因此打印出1、2、3共三行數字。

  當程序執行到第46行代碼處時,我們調用了t1的“Abort”方法,從而t1線程被終止。

  當程序執行到第49行代碼處時,由於已經在t1線程上調用了“Abort”方法,因此,它的狀態為“ThreadState.Aborted”或“ThreadState.AbortRequested”。

  當程序執行到第50行代碼處時,因為t2線程正常執行完畢,因此t2線程的狀態為“ThreadState.Stopped”。

六、線程優先級

   在這一小節中,我們將講述線程的優先級,線程的優先級確定了線程所能使用CPU時間的大小。我們按以下步驟來完成線程優先級的學習:

1、使用Visual Studio 2015創建一個新的控制台應用程序。

2、雙擊打開“Program.cs”文件,修改代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 using static System.Diagnostics.Process;
 6 
 7 namespace Recipe06
 8 {
 9     class ThreadSample
10     {
11         private bool isStopped = false;
12 
13         public void Stop()
14         {
15             isStopped = true;
16         }
17 
18         public void CountNumbers()
19         {
20             long counter = 0;
21             while (!isStopped)
22             {
23                 counter++;
24             }
25 
26             WriteLine($"{CurrentThread.Name} with {CurrentThread.Priority,10} priority has a count = {counter,15:N0}");
27         }
28     }
29 
30     class Program
31     {
32         static void RunThreads()
33         {
34             var sample = new ThreadSample();
35 
36             var threadOne = new Thread(sample.CountNumbers);
37             threadOne.Name = "ThreadOne";
38 
39             var threadTwo = new Thread(sample.CountNumbers);
40             threadTwo.Name = "ThreadTwo";
41 
42             threadOne.Priority = ThreadPriority.Highest;
43             threadTwo.Priority = ThreadPriority.Lowest;
44 
45             threadOne.Start();
46             threadTwo.Start();
47 
48             Sleep(TimeSpan.FromSeconds(2));
49             sample.Stop();
50         }
51 
52         static void Main(string[] args)
53         {
54             WriteLine($"Current thread priority: {CurrentThread.Priority}");
55             WriteLine("Running on all cores available");
56 
57             RunThreads();
58 
59             Sleep(TimeSpan.FromSeconds(2));
60 
61             WriteLine("Running on a single core");
62             GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
63             RunThreads();
64         }
65     }
66 }

3、運行該控制台應用程序,運行效果(每次運行效果可能不同)如下圖所示:

  在上述代碼中,我們定義了兩個不同的線程threadOne和threadTwo。threadOne線程具有最高的優先級“ThreadPriority.Highest”,threadTwo具有最低的優先級“ThreadPriority.Lowest”。

  在程序執行到第57行代碼處,如果我們的計算機是多核計算機,我們將在2秒內獲得初始結果,並且具有最高優先級的threadOne線程要比具有最低優先級的threadTwo線程的迭代次數更多一些,但也不會相差太遠。但是,如果我們的計算機是單核計算機,則結果大不相同。

  為了模擬單核計算機,我們將ProcessorAffinity的值設置為1,現在這兩個線程所迭代的次數將差異非常大,並且程序的執行時間遠遠大於2秒。這是因為CPU的計算機時間大部分給予了高優先級的線程,而低優先級的線程獲得非常少的CPU時間。

七、前台線程和後台線程

  在這一小節中,我們將講述什麼是前台線程和後台線程,並且講述如何設置這個選項以影響程序的行為。我們按以下步驟來完成前台線程和後台線程的學習:

1、使用Visual Studio 2015創建一個新的控制台應用程序。

2、雙擊打開“Program.cs”文件,修改代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe07
 7 {
 8     class ThreadSample
 9     {
10         private readonly int iterations;
11 
12         public ThreadSample(int iterations)
13         {
14             this.iterations = iterations;
15         }
16 
17         public void CountNumbers()
18         {
19             for(int i = 0; i < iterations; i++)
20             {
21                 Sleep(TimeSpan.FromSeconds(0.5));
22                 WriteLine($"{CurrentThread.Name} prints {i}");
23             }
24         }
25     }
26 
27     class Program
28     {
29         static void Main(string[] args)
30         {
31             var sampleForeground = new ThreadSample(10);
32             var sampleBackground = new ThreadSample(20);
33 
34             var threadOne = new Thread(sampleForeground.CountNumbers);
35             threadOne.Name = "ForegroundThread";
36 
37             var threadTwo = new Thread(sampleBackground.CountNumbers);
38             threadTwo.Name = "BackgroundThread";
39             threadTwo.IsBackground = true;
40 
41             threadOne.Start();
42             threadTwo.Start();
43         }
44     }
45 }

3、運行該控制台應用程序,運行效果(每次運行效果可能不同)如下圖所示:

  在上述代碼中,我們定義了兩個不同的線程threadOne和threadTwo。當我們創建一個線程時,該線程顯示地是一個前台線程,比如threadOne。如果我們要將一個線程設置為後台線程,只需要將該線程的“IsBackground”屬性設置為“true”即可,比如threadTwo。我們還配置了兩個線程執行的循環次數不一樣,threadOne線程所執行的循環次數為10次,threadTwo線程所執行的循環次數為20次,因此threadOne線程要比threadTwo線程早執行完畢。

  從執行結果上來看,當threadOne線程執行完畢後,主程序也結束了,並且後台線程也被終止了,這就是前台線程和後台線程的區別:進程會等待所有的前台進程執行完畢後才結束,單不會等待後台進程執行完畢。

八、向線程傳遞參數

   在這一小節,我將講述如何向一個線程傳遞參數,我們將使用不同的方法來完成這個工作,具體步驟如下所示:

1、使用Visual Studio 2015創建一個新的控制台應用程序。

2、雙擊打開“Program.cs”文件,修改代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe08
 7 {
 8     class ThreadSample
 9     {
10         private readonly int iterations;
11 
12         public ThreadSample(int iterations)
13         {
14             this.iterations = iterations;
15         }
16 
17         public void CountNumbers()
18         {
19             for(int i = 1; i <= iterations; i++)
20             {
21                 Sleep(TimeSpan.FromSeconds(0.5));
22                 WriteLine($"{CurrentThread.Name} prints {i}");
23             }
24         }
25     }
26 
27     class Program
28     {
29         static void Count(object iterations)
30         {
31             CountNumbers((int)iterations);
32         }
33 
34         static void CountNumbers(int iterations)
35         {
36             for(int i = 1; i <= iterations; i++)
37             {
38                 Sleep(TimeSpan.FromSeconds(0.5));
39                 WriteLine($"{CurrentThread.Name} prints {i}");
40             }
41         }
42 
43         static void PrintNumber(int number)
44         {
45             WriteLine(number);
46         }
47 
48         static void Main(string[] args)
49         {
50             var sample = new ThreadSample(10);
51 
52             var threadOne = new Thread(sample.CountNumbers);
53             threadOne.Name = "ThreadOne";
54             threadOne.Start();
55             threadOne.Join();
56 
57             WriteLine("--------------------------");
58 
59             var threadTwo = new Thread(Count);
60             threadTwo.Name = "ThreadTwo";
61             threadTwo.Start(8);
62             threadTwo.Join();
63 
64             WriteLine("--------------------------");
65 
66             var threadThree = new Thread(() => CountNumbers(12));
67             threadThree.Name = "ThreadThree";
68             threadThree.Start();
69             threadThree.Join();
70 
71             WriteLine("--------------------------");
72 
73             int i = 10;
74             var threadFour = new Thread(() => PrintNumber(i));
75 
76             i = 20;
77             var threadFive = new Thread(() => PrintNumber(i));
78 
79             threadFour.Start();
80             threadFive.Start();
81         }
82     }
83 }

3、運行該控制台應用程序,運行效果如下圖所示:

  在第50~55行代碼處,我們首先創建了一個ThreadSample類型的對象,並將數字10傳遞給了該類型的構造方法。然後我們將ThreadSample對象的“CountNumbers”方法傳遞給了線程threadOne的構造方法,“CountNumbers”方法將在線程threadOne中被執行,因此我們通過“CountNumbers”方法將數字10傳遞給了線程threadOne。

  在第59~62行代碼處,我們使用“Thread.Start”的重載方法將一個對象傳遞給線程threadTwo,要使該段代碼正確運行,我們要保證在構造threadTwo線程時,傳給給Thread的構造方法的委托要帶有一個object類型的參數。在這段代碼中,我們將8看作一個對象傳遞給“Count”方法,該方法有調用了同名的重載方法將object對象轉換為整數類型。

  在第66~69行代碼處,我們在創建threadThree線程時,給Thread構造方法傳遞的是一個lambda表達式,該表達式定義了一個不屬於任何類的方法,我們在該lambda表達式中調用了“CountNumbers”方法,並將12傳遞給了“CountNumbers”方法,通過lambda表達式,我們將12傳遞給了threadThree線程。

  注意:當我們將一個局部變量用於多個lambda表達式時,這些lambda表達式將會共享這個變量的值,在第73~80行代碼處,我們將局部變量用於了兩個lambda表達式,我們運行threadFour和threadFive線程後,我們發現打印出來的結果都是20。

   未完待續!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved