程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#多線程賽跑實例

C#多線程賽跑實例

編輯:C#入門知識

》,這次我們寫一個多線程的賽跑實例,內容很簡單:超人和蜘蛛俠賽跑,因為超人飛的比蜘蛛俠跳的快,為了公平,我們讓蜘蛛俠跑的長度小點,裁判負責宣布比賽的開始和結束。     [csharp]  class MultiThread       {           //定義兩個線程,分別為超人和蜘蛛俠            private static Thread SuperMan;           private static Thread SpiderMan;           //程序入口,比賽開始            static void Main(string[] args)           {               //初始化數據                InitData();               //裁判吹哨,開始賽跑                JudgeWork();           }              /// <summary>            /// 初始化超人和蜘蛛俠的線程和姓名            /// </summary>            private static void InitData()           {               SuperMan = new Thread(new ParameterizedThreadStart(RunnerWork));               SpiderMan = new Thread(new ParameterizedThreadStart(RunnerWork));               SuperMan.Name = "SuperMan";               SpiderMan.Name = "SpiderMan";              }           /// <summary>            /// 裁判開始比賽,最後宣布勝者            /// </summary>            private static void JudgeWork()           {               Console.WriteLine("{0}   PK   {1}", SuperMan.Name, SpiderMan.Name);               Console.WriteLine("比賽即將開始,請各位做好准備!");               Console.WriteLine("預備!");               Console.Read();               //Superman起跑                Console.WriteLine("回車槍響,Superman開始起跑!");               Console.Beep(654, 1200);               SuperMan.Start(500);               //Monster起跑                Console.WriteLine("回車槍響,SpiderMan開始起跑!");               SpiderMan.Start(200);               SuperMan.Join();               SpiderMan.Join();               //宣布賽跑結果                Console.WriteLine("我宣布比賽結束");               //程序暫停12秒                Thread.Sleep(12000);           }           /// <summary>            /// 賽跑的過程            /// </summary>            /// <param name="obj">賽跑參數</param>            private static void RunnerWork(Object obj)           {               int length = Int32.Parse(obj.ToString());               Thread CurrentThread = Thread.CurrentThread;               string CurThreadName = CurrentThread.Name;               int speed;               //超人速度為20                if (CurThreadName == SuperMan.Name)               {                   speed = 50;               }               //蜘蛛俠速度為20                else if (CurThreadName == SpiderMan.Name)               {                   speed = 20;               }               //如果不可控線程進入,采用以下速度                else               {                   speed = 1;               }               Console.WriteLine("{0},開始起跑…………", CurThreadName);               for (int count = speed; count <= length; count += speed)               {                   Thread.Sleep(1000);                   Console.WriteLine("{0}……跑到了第{1}米", CurThreadName, count.ToString());               }               Console.WriteLine("{0},到達終點!了咧歡迎……", CurThreadName);           }       }     class MultiThread     {         //定義兩個線程,分別為超人和蜘蛛俠         private static Thread SuperMan;         private static Thread SpiderMan;         //程序入口,比賽開始         static void Main(string[] args)         {             //初始化數據             InitData();             //裁判吹哨,開始賽跑             JudgeWork();         }           /// <summary>         /// 初始化超人和蜘蛛俠的線程和姓名         /// </summary>         private static void InitData()         {             SuperMan = new Thread(new ParameterizedThreadStart(RunnerWork));             SpiderMan = new Thread(new ParameterizedThreadStart(RunnerWork));             SuperMan.Name = "SuperMan";             SpiderMan.Name = "SpiderMan";           }         /// <summary>         /// 裁判開始比賽,最後宣布勝者         /// </summary>         private static void JudgeWork()         {             Console.WriteLine("{0}   PK   {1}", SuperMan.Name, SpiderMan.Name);             Console.WriteLine("比賽即將開始,請各位做好准備!");             Console.WriteLine("預備!");             Console.Read();             //Superman起跑             Console.WriteLine("回車槍響,Superman開始起跑!");             Console.Beep(654, 1200);             SuperMan.Start(500);             //Monster起跑             Console.WriteLine("回車槍響,SpiderMan開始起跑!");             SpiderMan.Start(200);             SuperMan.Join();             SpiderMan.Join();             //宣布賽跑結果             Console.WriteLine("我宣布比賽結束");             //程序暫停12秒             Thread.Sleep(12000);         }         /// <summary>         /// 賽跑的過程         /// </summary>         /// <param name="obj">賽跑參數</param>         private static void RunnerWork(Object obj)         {             int length = Int32.Parse(obj.ToString());             Thread CurrentThread = Thread.CurrentThread;             string CurThreadName = CurrentThread.Name;             int speed;             //超人速度為20             if (CurThreadName == SuperMan.Name)             {                 speed = 50;             }             //蜘蛛俠速度為20             else if (CurThreadName == SpiderMan.Name)             {                 speed = 20;             }             //如果不可控線程進入,采用以下速度             else             {                 speed = 1;             }             Console.WriteLine("{0},開始起跑…………", CurThreadName);             for (int count = speed; count <= length; count += speed)             {                 Thread.Sleep(1000);                 Console.WriteLine("{0}……跑到了第{1}米", CurThreadName, count.ToString());             }             Console.WriteLine("{0},到達終點!了咧歡迎……", CurThreadName);         }     }        運行結果:            \                 比賽剛剛開始,裁判即宣布結束,這不符合常理。仔細分析可以發現,程序可控制的進程一共有三個,即裁判、超人和蜘蛛俠,三個進程相互獨立同時進行,所以裁判宣布比賽開始後即按照它的線程繼續宣布結束。         我們可以這樣:在裁判宣布比賽開始後,讓蜘蛛俠和超人的線程執行完畢再執行裁判進程:   [csharp]   //防止裁判的主進程先結束,讓超人和蜘蛛俠的進程先執行完畢    SuperMan.Join();   SpiderMan.Join();   Console.WriteLine("我宣布比賽結束");     //防止裁判的主進程先結束,讓超人和蜘蛛俠的進程先執行完畢 SuperMan.Join(); SpiderMan.Join(); Console.WriteLine("我宣布比賽結束");        這次的執行結果為:             \           賽跑結束,裁判才宣布比賽結束,但是還有問題,裁判總得宣布誰跑贏了吧,台底下這麼多粉絲等著呢?這個我們可以用變量的方式保存署名,達到宣布誰為冠軍的功能。         為了展示同步異步讀寫問題,我們讓超人賽跑中去拯救世界,然後回來繼續比賽;先到達終點的人,自己花時間找粉筆,然後在黑板上署名,其他人看到黑板上有名字就不能再寫,裁判宣布署名的人為勝者。       [csharp]   class MultiThread3   {          //署名用的黑板        static string NameBoard = "";       //定義兩個線程,分別為超人和蜘蛛俠        private static Thread SuperMan;       private static Thread SpiderMan;       //程序入口,比賽開始        static void Main(string[] args)       {           //初始化數據            InitData();           //裁判吹哨,開始賽跑            JudgeWork();       }          /// <summary>        /// 初始化超人和蜘蛛俠的線程和姓名        /// </summary>        private static void InitData()       {           SuperMan = new Thread(new ParameterizedThreadStart(RunnerWork));           SpiderMan = new Thread(new ParameterizedThreadStart(RunnerWork));           SuperMan.Name = "SuperMan";           SpiderMan.Name = "SpiderMan";          }       /// <summary>        /// 裁判開始比賽,最後宣布勝者        /// </summary>        private static void JudgeWork()       {           Console.WriteLine("{0}   PK   {1}", SuperMan.Name, SpiderMan.Name);           Console.WriteLine("比賽即將開始,請各位做好准備!");           Console.WriteLine("預備!");           Console.Read();           //Superman起跑            Console.WriteLine("回車槍響,SuperMan開始起跑!");           Console.Beep(654, 1200);           SuperMan.Start(500);           //Monster起跑            Console.WriteLine("回車槍響,SpiderMan開始起跑!");           SpiderMan.Start(300);           //防止裁判的主進程先結束,讓超人和蜘蛛俠的進程先執行完畢            SuperMan.Join();           SpiderMan.Join();           //宣布賽跑結果            AnnounceWinner();           //程序暫停12秒            Thread.Sleep(12000);       }       /// <summary>        /// 賽跑的過程        /// </summary>        /// <param name="obj">賽跑參數</param>        private static void RunnerWork(Object obj)       {           int length = Int32.Parse(obj.ToString());           Thread CurrentThread = Thread.CurrentThread;           string CurThreadName = CurrentThread.Name;           int speed;           //超人速度為20            if (CurThreadName == SuperMan.Name)           {               speed = 50;           }           //蜘蛛俠速度為20            else if (CurThreadName == SpiderMan.Name)           {               speed = 20;           }           //如果不可控線程進入,采用以下速度            else           {               speed = 1;           }           Console.WriteLine("{0},開始起跑…………", CurThreadName);           for (int count = speed; count <= length; count += speed)           {               Thread.Sleep(1000);               Console.WriteLine("{0}……跑到了第{1}米", CurThreadName, count.ToString());               //超人跑到一半,去拯救世界                if (count == length / 2)               {                   if (CurThreadName == SuperMan.Name)                   {                       Console.WriteLine("世界末日來臨,超人去拯救世界……");                       string waitInfo = "..";                       //超人拯救世界過程                        for (int j = 0; j <= 10; j++)                       {                           Console.WriteLine("超人拯救世界中" + waitInfo);                           waitInfo += "..";                           Thread.Sleep(1000);                       }                        Console.WriteLine("超人去拯救世界歸來,繼續賽跑……");                   }               }           }           Console.WriteLine("{0},到達終點!樂咧歡迎……", CurThreadName);           WriteName(CurThreadName);       }          /// <summary>        /// 跑到重點線後,選手自己在黑板上署名        /// </summary>         /// <param name="name">選手姓名</param>        private static void WriteName(string name)       {           //黑板上沒名字,才可以署自己的名字            if (NameBoard.Length == 0)           {               Console.WriteLine("{0}去找粉筆了……", name);               //找粉筆花費的時間                Thread.Sleep(9000);               Console.WriteLine("{0}拿著粉筆回來了,開始署名……", name);               NameBoard = name;               Console.WriteLine("{0}署完名後,開心的離開了……", name);           }           //黑板上有署名時不能再署名            else           {               Console.WriteLine("{0}發現已經署名,桑心的離開了……", name);           }       }       /// <summary>        /// 宣布比賽結果        /// </summary>        private static void AnnounceWinner()       {           Console.WriteLine("我是裁判,我宣布這次比賽的冠軍是{0}", NameBoard);       }   }     class MultiThread3 {       //署名用的黑板     static string NameBoard = "";     //定義兩個線程,分別為超人和蜘蛛俠     private static Thread SuperMan;     private static Thread SpiderMan;     //程序入口,比賽開始     static void Main(string[] args)     {         //初始化數據         InitData();         //裁判吹哨,開始賽跑         JudgeWork();     }       /// <summary>     /// 初始化超人和蜘蛛俠的線程和姓名     /// </summary>     private static void InitData()     {         SuperMan = new Thread(new ParameterizedThreadStart(RunnerWork));         SpiderMan = new Thread(new ParameterizedThreadStart(RunnerWork));         SuperMan.Name = "SuperMan";         SpiderMan.Name = "SpiderMan";       }     /// <summary>     /// 裁判開始比賽,最後宣布勝者     /// </summary>     private static void JudgeWork()     {         Console.WriteLine("{0}   PK   {1}", SuperMan.Name, SpiderMan.Name);         Console.WriteLine("比賽即將開始,請各位做好准備!");         Console.WriteLine("預備!");         Console.Read();         //Superman起跑         Console.WriteLine("回車槍響,SuperMan開始起跑!");         Console.Beep(654, 1200);         SuperMan.Start(500);         //Monster起跑         Console.WriteLine("回車槍響,SpiderMan開始起跑!");         SpiderMan.Start(300);         //防止裁判的主進程先結束,讓超人和蜘蛛俠的進程先執行完畢         SuperMan.Join();         SpiderMan.Join();         //宣布賽跑結果         AnnounceWinner();         //程序暫停12秒         Thread.Sleep(12000);     }     /// <summary>     /// 賽跑的過程     /// </summary>     /// <param name="obj">賽跑參數</param>     private static void RunnerWork(Object obj)     {         int length = Int32.Parse(obj.ToString());         Thread CurrentThread = Thread.CurrentThread;         string CurThreadName = CurrentThread.Name;         int speed;         //超人速度為20         if (CurThreadName == SuperMan.Name)         {             speed = 50;         }         //蜘蛛俠速度為20         else if (CurThreadName == SpiderMan.Name)         {             speed = 20;         }         //如果不可控線程進入,采用以下速度         else         {             speed = 1;         }         Console.WriteLine("{0},開始起跑…………", CurThreadName);         for (int count = speed; count <= length; count += speed)         {             Thread.Sleep(1000);             Console.WriteLine("{0}……跑到了第{1}米", CurThreadName, count.ToString());             //超人跑到一半,去拯救世界             if (count == length / 2)             {                 if (CurThreadName == SuperMan.Name)                 {                     Console.WriteLine("世界末日來臨,超人去拯救世界……");                     string waitInfo = "..";                     //超人拯救世界過程                     for (int j = 0; j <= 10; j++)                     {                         Console.WriteLine("超人拯救世界中" + waitInfo);                         waitInfo += "..";                         Thread.Sleep(1000);                     }                      Console.WriteLine("超人去拯救世界歸來,繼續賽跑……");                 }             }         }         Console.WriteLine("{0},到達終點!樂咧歡迎……", CurThreadName);         WriteName(CurThreadName);     }       /// <summary>     /// 跑到重點線後,選手自己在黑板上署名     /// </summary>      /// <param name="name">選手姓名</param>     private static void WriteName(string name)     {         //黑板上沒名字,才可以署自己的名字         if (NameBoard.Length == 0)         {             Console.WriteLine("{0}去找粉筆了……", name);             //找粉筆花費的時間             Thread.Sleep(9000);             Console.WriteLine("{0}拿著粉筆回來了,開始署名……", name);             NameBoard = name;             Console.WriteLine("{0}署完名後,開心的離開了……", name);         }         //黑板上有署名時不能再署名         else         {             Console.WriteLine("{0}發現已經署名,桑心的離開了……", name);         }     }     /// <summary>     /// 宣布比賽結果     /// </summary>     private static void AnnounceWinner()     {         Console.WriteLine("我是裁判,我宣布這次比賽的冠軍是{0}", NameBoard);     } }        運行結果:            \
\                      可以看到明明是SuperMan還在拯救地球時,SpiderMan已經到達終點,而裁判宣布的冠軍卻是SuperMan。仔細分析一下程序即可知道:雖然SpiderMan先到達終點,並且先發現黑板是空的,但是在SpiderMan尋找粉筆的過程中,SuperMan到達終點,並且也發現黑板是空的,於是兩人都寫上了自己的名字,但是因為後者的會覆蓋前者的,所以勝利者成了SuperMan,整個過程如下圖所示:   \                      問題出現的原因在於,SpiderMan到達以後看到黑板,SuperMan仍然看到黑板,即這個黑板對於兩個人都是可寫的,後者會覆蓋前者的內容,這種方式為異步寫。         怎麼克服這個問題?可以使用Lock鎖住臨界區代碼,如下:   [csharp]   //定義一個對象類型的objLock    private static object objLock = new object();   /// <summary>    /// 跑到重點線後,選手自己在黑板上署名    /// </summary>     /// <param name="name">選手姓名</param>    private static void WriteName(string name)   {       //采用異步讀方式,篩選掉已經看到署名的線程,提高效率        //黑板上沒名字,才可以署自己的名字        if (NameBoard.Length == 0)       {           //因為上面為異步讀,所以可能多個線程可以進入到這一步            lock (objLock)           {               //同步讀方式                if (NameBoard.Length == 0)               {                   Console.WriteLine("{0}去找粉筆了……", name);                   //找粉筆花費的時間                    Thread.Sleep(9000);                   Console.WriteLine("{0}拿著粉筆回來了,開始署名……", name);                   NameBoard = name;                   Console.WriteLine("{0}署完名後,開心地離開了……", name);               }               //黑板上有署名時不能再署名                else               {                   Console.WriteLine("{0}發現已經署名,桑心地離開了……", name);               }           }       }   }             //定義一個對象類型的objLock         private static object objLock = new object();         /// <summary>         /// 跑到重點線後,選手自己在黑板上署名         /// </summary>          /// <param name="name">選手姓名</param>         private static void WriteName(string name)         {             //采用異步讀方式,篩選掉已經看到署名的線程,提高效率             //黑板上沒名字,才可以署自己的名字             if (NameBoard.Length == 0)             {                 //因為上面為異步讀,所以可能多個線程可以進入到這一步                 lock (objLock)                 {                     //同步讀方式                     if (NameBoard.Length == 0)                     {                         Console.WriteLine("{0}去找粉筆了……", name);                         //找粉筆花費的時間                         Thread.Sleep(9000);                         Console.WriteLine("{0}拿著粉筆回來了,開始署名……", name);                         NameBoard = name;                         Console.WriteLine("{0}署完名後,開心地離開了……", name);                     }                     //黑板上有署名時不能再署名                     else                     {                         Console.WriteLine("{0}發現已經署名,桑心地離開了……", name);                     }                 }             }         }              需要注意的是,鎖住的內容(非臨界區代碼)必須是共享型的引用型數據,因為如果是局部變量針對一個線程鎖不鎖對其它線程意義不大;采用引用數據類型,可以保證每個線程鎖住內容都指向同一個地址。           為了直觀顯示,沒有抽象出超人、蜘蛛俠和裁判的類,以上就是一個簡單的多線程應用實例,當然這是多線程的冰山一角,更多的還有待在以後開發中實踐,這次爭取在考試系統使用多線程優化抽題和判分等功能。  

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