用《捕魚達人》去理解C#中的多線程

線程是進程中某個單一順序的控制流,是程序運行中的調度單位,是程序執行流的最小單位,一個標準的線程由線程ID,當前指令指針(PC),寄存器集合和堆棧組成。 線程本身不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的所有資源。 線程也有就緒、阻塞和運行三種基本狀態。每個程序都至少有一個線程,若程序只有一個線程,那就是程序進程自己。多線程

CLR中有三種經常使用建立和管理線程的方式:Thread、ThreadPool、Task,下面用最簡單的例子寫出本身對這三種方式的理解:

1、Thread

《捕魚達人》是你們都玩過的遊戲,至於遊戲怎麼設計我也不太清楚,但我想在這裏用本身對線程的理解來用線程描述這個遊戲。假如屏幕上隨機產生兩條魚,而且游來游去,代碼以下:
 1       class Fish
 2       {
 3             public string Name { get; set; }
 4  
 5             public Fish()
 6             {
 7                   Name = "小黃魚" ;
 8             }
 9  
10             public void Move()
11             {
12                   Console.WriteLine(string .Format("{0}在游來游去......", Name));
13             }
14       }
15  
16       class Program
17       {
18             static void Main(string[] args)
19             {
20                   Fish fish = new Fish();
21                   Thread t1 = new Thread(() =>
22                   {
23                         fish.Move();
24                   });
25                   t1.IsBackground = true;
26                   t1.Start();
27  
28                   Fish fish2 = new Fish() { Name = "大鯊魚" };
29                   Thread t2 = new Thread(() =>
30                   {
31                         fish2.Move();
32                   });
33                   t2.IsBackground = true;
34                   t2.Start();
35  
36                   Console.ReadKey();
37             }
38       }
運行後屏幕以下:
小黃魚在游來游去......
大鯊魚在游來游去...... 

2、ThreadPool

若是魚潮來臨,一會兒要產生100條魚,若是按上面Thread的作法就要開啓100條線程,這樣對系統資源的損耗太大,這時咱們能夠用ThreadPool線程池來實現,代碼以下:
 1             static void Main(string[] args)
 2             {
 3                    Fish fish = new Fish();
 4                    Fish fish2 = new Fish() { Name = "大鯊魚" };
 5                    Fish fish3 = new Fish() { Name = "燈籠魚" };
 6                    Fish fish4 = new Fish() { Name = "紅鯉魚" };
 7                    Fish fish100 = new Fish() { Name = "燈籠魚" };
 8                    ThreadPool.QueueUserWorkItem(f => { fish.Move(); });
 9                    ThreadPool.QueueUserWorkItem(f => { fish2.Move(); });
10                    ThreadPool.QueueUserWorkItem(f => { fish3.Move(); });
11                    ThreadPool.QueueUserWorkItem(f => { fish4.Move(); });
12                    ThreadPool.QueueUserWorkItem(f => { fish100.Move(); });
13                    Console.ReadKey();
14             }

運行後屏幕以下:併發

燈籠魚在游來游去......
大鯊魚在游來游去......
燈籠魚在游來游去......
小黃魚在游來游去......
紅鯉魚在游來游去......
因爲多線程是併發執行,由系統分配順序,因此上面的結果是隨機的  

3、Task

Task是.Net4.0中新加的功能,因爲ThreadPool對池中的線程很差控制,Task用來彌補,好比在魚在流動的時候,我開了一個槍和炮的線程用來發射子彈捕魚,魚中槍後魚遊動的線程就要結束,結束的時候彈出獎勵積分,好比小黃魚彈出1分,大鯊魚彈出100分,這是就要用到Task對象的ContinueWith方法,該方法能夠在線程結束的時候產生一個回調方法,代碼以下:
 
  1     class Program
  2       {
  3             static void Main(string[] args)
  4             {
  5                   //用來取消小黃魚線程
  6                   CancellationTokenSource cts = new CancellationTokenSource ();
  7  
  8                   Fish fish = new Fish();
  9                   Fish fish2 = new Fish() { Name = "大鯊魚" , Score =100 };
 10  
 11                   Task t1 = new Task(() => fish.Move(cts.Token), cts.Token);
 12                   t1.Start();
 13                   //小黃魚被擊中後顯示積分
 14                   t1.ContinueWith(fish.ShowScore);
 15  
 16                   Task t2 = new Task(() =>fish2.Move(cts.Token), cts.Token);             
 17                   t2.Start();
 18                   //大鯊魚魚被擊中後顯示積分
 19                   t2.ContinueWith(fish2.ShowScore);
 20  
 21                   //按任意鍵發射
 22                   Console.ReadKey();
 23  
 24                   //武器工廠線程池,執行一組任務
 25                   Gun gun = new Gun();
 26                   LaserGun laserGun = new LaserGun();
 27                   TaskFactory taskfactory = new TaskFactory();
 28                   Task[] tasks = new Task[]
 29                   {
 30                         taskfactory.StartNew(()=>gun.Fire()),
 31                         taskfactory.StartNew(()=>laserGun.Fire())
 32                   };
 33                   //執行武器們開火
 34                   taskfactory.ContinueWhenAll(tasks, (Task) => { });
 35  
 36                   //魚兒們被擊中了就會去調顯示積分的方法
 37                   cts.Cancel();
 38                   Console.ReadLine();
 39             }
 40       }
 41  
 42       class Fish
 43       {
 44             public string Name { get; set; }
 45             public int Score { get; set; }
 46  
 47             public Fish()
 48             {
 49                   Name = "小黃魚" ;
 50                   Score = 1;
 51             }
 52  
 53             /// <summary>
 54             /// 遊動
 55             /// </summary>
 56             public void Move(CancellationToken ct)
 57             {
 58                   //若是沒有被擊中,就一直遊阿遊,用IsCancellationRequested判斷
 59                   while (!ct.IsCancellationRequested)
 60                   {
 61                          Console.WriteLine(string .Format("{0}在游來游去......", Name));
 62                          Thread.Sleep(1000);
 63                   }                 
 64             }
 65  
 66             //中槍死亡後顯示獎勵
 67             public void ShowScore(Task task)
 68             {
 69                    Console.WriteLine(string .Format("{0}中彈了,您獲得{1}分......" , Name, Score));
 70             }
 71       }
 72  
 73       abstract class Weapon
 74       {
 75              public string Name { get; set; }
 76              public abstract void Fire();
 77       }
 78  
 79       class Gun : Weapon
 80       {
 81             public Gun()
 82                   : base()
 83             {
 84                   Name = "雙射槍" ;
 85             }
 86             public override void Fire()
 87             {
 88                   Console.WriteLine(string .Format("咻咻咻,{0}向魚兒們發射子彈......" , Name));
 89             }
 90       }
 91  
 92       class LaserGun : Weapon
 93       {
 94             public LaserGun()
 95                   : base()
 96             {
 97                   Name = "激光炮" ;
 98             }
 99             public override void Fire()
100             {
101                   Console.WriteLine(string .Format("嗖嗖嗖,{0}向魚兒們發射炮彈......" , Name));
102             }
103       }   
運行後屏幕以下:
大鯊魚在游來游去......
小黃魚在游來游去......
大鯊魚在游來游去......
小黃魚在游來游去......
大鯊魚在游來游去......
小黃魚在游來游去...... 
按任意鍵開火後屏幕顯示:
大鯊魚在游來游去......
小黃魚在游來游去......
大鯊魚在游來游去......
小黃魚在游來游去......
大鯊魚在游來游去......
小黃魚在游來游去......
咻咻咻,雙射槍向魚兒們發射子彈......
嗖嗖嗖,激光炮向魚兒們發射子彈......
大鯊魚中彈了,您獲得100分......
小黃魚中彈了,您獲得1分......  

總結:

本人技術通常,腦子愚鈍,不少複雜的資料看不太懂,因此喜歡用簡單的例子幫助本身理解某個知識點,上面的例子有不少的不足之處,大神們要看到不要笑話就行。
用到線程的時候,若是數量少就用Thread,但Thread不太好結束,這時能夠考慮用CancellationTokenSource 類的Cancel方法,當要用到不少線程的時候就用線程池ThreadPool,當要用到不少線程且要對對應的線程進行控制和回調的時候就用Task。
有關Thread、ThreadPool、Task的詳細資料你們能夠看官方的資料。
若是本文幫到了你或者讓你輕輕地笑了下,就幫點個推薦吧。
謝謝閱讀。 
相關文章
相關標籤/搜索