1. 程式人生 > >用《捕魚達人》去理解C#中的多執行緒

用《捕魚達人》去理解C#中的多執行緒

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

CLR中有三種常用建立和管理執行緒的方式:Thread、ThreadPool、Task,下面用最簡單的例子寫出自己對這三種方式的理解:

一、Thread

《捕魚達人》是大家都玩過的遊戲,至於遊戲怎麼設計我也不太清楚,但我想在這裡用自己對執行緒的理解來用執行緒描述這個遊戲。假如螢幕上隨機產生兩條魚,並且游來游去,程式碼如下:
class
Fish { public string Name { get; set; } public Fish() { Name = "小黃魚" ; } public void Move() { Console.WriteLine(string .Format("{0}在游來游去......", Name)); } }
class Program { static void Main(string[] args) { Fish fish = new Fish(); Thread t1 = new Thread(() => { fish.Move(); }); t1.IsBackground = true
; t1.Start(); Fish fish2 = new Fish() { Name = "大鯊魚" }; Thread t2 = new Thread(() => { fish2.Move(); }); t2.IsBackground = true; t2.Start(); Console.ReadKey(); } }
執行後螢幕如下: 小黃魚在游來游去...... 大鯊魚在游來游去......   

二、ThreadPool

如果魚潮來臨,一下子要產生100條魚,如果按上面Thread的做法就要開啟100條執行緒,這樣對系統資源的損耗太大,這時我們可以用ThreadPool執行緒池來實現,程式碼如下:
static void Main(string[] args)
            {
                   Fish fish = new Fish();
                   Fish fish2 = new Fish() { Name = "大鯊魚" };
                   Fish fish3 = new Fish() { Name = "燈籠魚" };
                   Fish fish4 = new Fish() { Name = "紅鯉魚" };
                   Fish fish100 = new Fish() { Name = "燈籠魚" };
                   ThreadPool.QueueUserWorkItem(f => { fish.Move(); });
                   ThreadPool.QueueUserWorkItem(f => { fish2.Move(); });
                   ThreadPool.QueueUserWorkItem(f => { fish3.Move(); });
                   ThreadPool.QueueUserWorkItem(f => { fish4.Move(); });
                   ThreadPool.QueueUserWorkItem(f => { fish100.Move(); });
                   Console.ReadKey();
            }

執行後螢幕如下:

燈籠魚在游來游去...... 大鯊魚在游來游去...... 燈籠魚在游來游去...... 小黃魚在游來游去...... 紅鯉魚在游來游去...... 由於多執行緒是併發執行,由系統分配順序,所以上面的結果是隨機的 

三、Task

Task是.Net4.0中新加的功能,由於ThreadPool對池中的執行緒不好控制,Task用來彌補,比如在魚在流動的時候,我開了一個槍和炮的執行緒用來發射子彈捕魚,魚中槍後魚遊動的執行緒就要結束,結束的時候彈出獎勵積分,比如小黃魚彈出1分,大鯊魚彈出100分,這是就要用到Task物件的ContinueWith方法,該方法可以線上程結束的時候產生一個回撥方法,程式碼如下:
class Program
      {
            static void Main(string[] args)
            {
                  //用來取消小黃魚執行緒
                  CancellationTokenSource cts = new CancellationTokenSource ();
 
                  Fish fish = new Fish();
                  Fish fish2 = new Fish() { Name = "大鯊魚" , Score =100 };
 
                  Task t1 = new Task(() => fish.Move(cts.Token), cts.Token);
                  t1.Start();
                  //小黃魚被擊中後顯示積分
                  t1.ContinueWith(fish.ShowScore);
 
                  Task t2 = new Task(() =>fish2.Move(cts.Token), cts.Token);             
                  t2.Start();
                  //大鯊魚魚被擊中後顯示積分
                  t2.ContinueWith(fish2.ShowScore);
 
                  //按任意鍵發射
                  Console.ReadKey();
 
                  //武器工廠執行緒池,執行一組任務
                  Gun gun = new Gun();
                  LaserGun laserGun = new LaserGun();
                  TaskFactory taskfactory = new TaskFactory();
                  Task[] tasks = new Task[]
                  {
                        taskfactory.StartNew(()=>gun.Fire()),
                        taskfactory.StartNew(()=>laserGun.Fire())
                  };
                  //執行武器們開火
                  taskfactory.ContinueWhenAll(tasks, (Task) => { });
 
                  //魚兒們被擊中了就會去調顯示積分的方法
                  cts.Cancel();
                  Console.ReadLine();
            }
      }
 
      class Fish
      {
            public string Name { get; set; }
            public int Score { get; set; }
 
            public Fish()
            {
                  Name = "小黃魚" ;
                  Score = 1;
            }
 
            /// <summary>
            /// 遊動
            /// </summary>
            public void Move(CancellationToken ct)
            {
                  //如果沒有被擊中,就一直遊阿遊,用IsCancellationRequested判斷
                  while (!ct.IsCancellationRequested)
                  {
                         Console.WriteLine(string .Format("{0}在游來游去......", Name));
                         Thread.Sleep(1000);
                  }                 
            }
 
            //中槍死亡後顯示獎勵
            public void ShowScore(Task task)
            {
                   Console.WriteLine(string .Format("{0}中彈了,您得到{1}分......" , Name, Score));
            }
      }
 
      abstract class Weapon
      {
             public string Name { get; set; }
             public abstract void Fire();
      }
 
      class Gun : Weapon
      {
            public Gun()
                  : base()
            {
                  Name = "雙射槍" ;
            }
            public override void Fire()
            {
                  Console.WriteLine(string .Format("咻咻咻,{0}向魚兒們發射子彈......" , Name));
            }
      }
 
      class LaserGun : Weapon
      {
            public LaserGun()
                  : base()
            {
                  Name = "鐳射炮" ;
            }
            public override void Fire()
            {
                  Console.WriteLine(string .Format("嗖嗖嗖,{0}向魚兒們發射炮彈......" , Name));
            }
      }
執行後螢幕如下: 大鯊魚在游來游去...... 小黃魚在游來游去...... 大鯊魚在游來游去...... 小黃魚在游來游去...... 大鯊魚在游來游去...... 小黃魚在游來游去......  按任意鍵開火後螢幕顯示: 大鯊魚在游來游去...... 小黃魚在游來游去...... 大鯊魚在游來游去...... 小黃魚在游來游去...... 大鯊魚在游來游去...... 小黃魚在游來游去...... 咻咻咻,雙射槍向魚兒們發射子彈...... 嗖嗖嗖,鐳射炮向魚兒們發射子彈...... 大鯊魚中彈了,您得到100分...... 小黃魚中彈了,您得到1分......      from:http://www.cnblogs.com/maitian-lf/p/3678128.html#undefined