多線程編程(1):線程的啓動

在實例化Thread的實例,需要提供一個委託,在實例化這個委託時所用到的參數是線程將來啓動時要運行的方法。在.net中提供了兩種啓動線程的方式,一種是不帶參數的啓動方式,另一種是帶參數的啓動的方式。
 不帶參數的啓動方式
 如果啓動參數時無需其它額外的信息,可以使用ThreadStart來實例化Thread,如下面的代碼:

view plaincopy to clipboardprint?
using System;  
    using System.Collections.Generic;  
    using System.Text;  
    using System.Threading;  
      
    namespace StartThread  
    {  
        class Program  
        {  
            int interval = 200;  
            static void Main(string[] args)  
            {  
                Program p = new Program();  
                Thread nonParameterThread = new Thread(new ThreadStart(p.NonParameterRun));  
                nonParameterThread.Start();  
            }  
            /// <summary>  
            /// 不帶參數的啓動方法  
            /// </summary>  
            public void NonParameterRun()  
            {  
                for (int i = 0; i < 10; i++)  
                {  
                    Console.WriteLine("系統當前時間毫秒值:"+DateTime.Now.Millisecond.ToString());  
                    Thread.Sleep(interval);//讓線程暫停  
                }  
        }  
    } 
using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Threading;
 
 namespace StartThread
 {
     class Program
     {
         int interval = 200;
         static void Main(string[] args)
         {
             Program p = new Program();
             Thread nonParameterThread = new Thread(new ThreadStart(p.NonParameterRun));
             nonParameterThread.Start();
         }
         /// <summary>
         /// 不帶參數的啓動方法
         /// </summary>
         public void NonParameterRun()
         {
             for (int i = 0; i < 10; i++)
             {
                 Console.WriteLine("系統當前時間毫秒值:"+DateTime.Now.Millisecond.ToString());
                 Thread.Sleep(interval);//讓線程暫停
             }
     }
 }

程序的運行效果我們不用運行也會知道,那就是在循環中將系統當前時間的毫秒部分輸出出來,在每次輸出之後會將當前線程暫停一下,直到10次之後運行完畢,終止線程的執行。
 在上面的代碼中我們是通過定義全局變量的方法來指定線程暫停間隔,按照這種方法,假如要運行10個線程,每個線程的暫停間隔不一樣的話,就需要定義10個全局變量,雖然最終不影響系統的運行效果,但是總覺得不是太爽。
 有沒有比較簡單一點的辦法呢?有!那就是使用帶參數的啓動方法。
 帶參數的啓動方法
 如果要在實例化線程時要帶一些參數,就不能用ThreadStart委託作爲構造函數的參數來實例化Thread了,而要ParameterizedThreadStart委託,和ThreadStart一樣的是它也是線程啓動時要執行的方法,和ThreadStart不同的是,它在實例化時可以用一個帶有一個Object參數的方法作爲構造函數的參數,而實例化ThreadStart時所用到的方法是沒有參數的。
 爲什麼是Object這樣的參數呢?很簡單,因爲在.net中Object是所有類型的基類,用它可以表示Array(數組)、Interface(接口)、ValueType(值類型,如bool,byte,char,short,int,float,long,double等)、class(類)等.net中的類型。當然,這也意味着如果你要啓動一個線程,給它傳遞一個int類型參數時,必須在啓動方法中進行相應的類型轉換。
 下面就是一個例子,在啓動線程時指定了線程的暫停間隔,代碼如下:

view plaincopy to clipboardprint?
using System;  
    using System.Collections.Generic;  
    using System.Text;  
    using System.Threading;  
      
    namespace StartThread  
    {  
        class Program  
        {  
            int interval = 200;  
            static void Main(string[] args)  
            {  
                Program p = new Program();  
      
                Thread parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));  
                parameterThread.Name = "Thread A:";  
                parameterThread.Start(30);  
            }  
         
            /// <summary>  
            /// 帶參數的啓動方法  
            /// </summary>  
            /// <param name="ms">讓線程在運行過程中的休眠間隔</param>  
            public void ParameterRun(object ms)  
            {  
                int j = 10;  
                int.TryParse(ms.ToString(), out j);//這裏採用了TryParse方法,避免不能轉換時出現異常  
                for (int i = 0; i < 10; i++)  
                {  
                    Console.WriteLine(Thread.CurrentThread.Name+"系統當前時間毫秒值:" + DateTime.Now.Millisecond.ToString());  
                    Thread.Sleep(j);//讓線程暫停  
                }  
            }  
        }  
    } 
using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Threading;
 
 namespace StartThread
 {
     class Program
     {
         int interval = 200;
         static void Main(string[] args)
         {
             Program p = new Program();
 
             Thread parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));
             parameterThread.Name = "Thread A:";
             parameterThread.Start(30);
         }
   
         /// <summary>
         /// 帶參數的啓動方法
         /// </summary>
         /// <param name="ms">讓線程在運行過程中的休眠間隔</param>
         public void ParameterRun(object ms)
         {
             int j = 10;
             int.TryParse(ms.ToString(), out j);//這裏採用了TryParse方法,避免不能轉換時出現異常
             for (int i = 0; i < 10; i++)
             {
                 Console.WriteLine(Thread.CurrentThread.Name+"系統當前時間毫秒值:" + DateTime.Now.Millisecond.ToString());
                 Thread.Sleep(j);//讓線程暫停
             }
         }
     }
 }

在這個方法裏,我們在啓動線程時順便指定了線程的暫停間隔,也就是這句:
 parameterThread.Start(30);
 線程啓動時運行的方法是public void ParameterRun(object ms),這個值爲30的int類型變量被裝箱成object,所以在方法中還需要將它轉換成int類型,這個可以通過拆箱或者其它辦法解決。
 假如我們要啓動兩個線程,每個線程的暫停間隔不一樣,啓動代碼如下:

view plaincopy to clipboardprint?
using System;  
    using System.Collections.Generic;  
    using System.Text;  
    using System.Threading;  
      
    namespace StartThread  
    {  
        class Program  
        {  
            int interval = 200;  
            static void Main(string[] args)  
            {  
                Program p = new Program();  
      
                Thread parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));  
                parameterThread.Name = "Thread A:";  
                parameterThread.Start(30);  
                //啓動第二個線程  
                parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));  
                parameterThread.Name = "Thread B:";  
                parameterThread.Start(60);  
            }  
         
            /// <summary>  
            /// 帶參數的啓動方法  
            /// </summary>  
            /// <param name="ms">讓線程在運行過程中的休眠間隔</param>  
            public void ParameterRun(object ms)  
            {  
                int j = 10;  
                int.TryParse(ms.ToString(), out j);//這裏採用了TryParse方法,避免不能轉換時出現異常  
                for (int i = 0; i < 10; i++)  
                {  
                    Console.WriteLine(Thread.CurrentThread.Name+"系統當前時間毫秒值:" + DateTime.Now.Millisecond.ToString());  
                    Thread.Sleep(j);//讓線程暫停  
                }  
            }  
        }  
    } 
using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Threading;
 
 namespace StartThread
 {
     class Program
     {
         int interval = 200;
         static void Main(string[] args)
         {
             Program p = new Program();
 
             Thread parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));
             parameterThread.Name = "Thread A:";
             parameterThread.Start(30);
             //啓動第二個線程
             parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));
             parameterThread.Name = "Thread B:";
             parameterThread.Start(60);
         }
   
         /// <summary>
         /// 帶參數的啓動方法
         /// </summary>
         /// <param name="ms">讓線程在運行過程中的休眠間隔</param>
         public void ParameterRun(object ms)
         {
             int j = 10;
             int.TryParse(ms.ToString(), out j);//這裏採用了TryParse方法,避免不能轉換時出現異常
             for (int i = 0; i < 10; i++)
             {
                 Console.WriteLine(Thread.CurrentThread.Name+"系統當前時間毫秒值:" + DateTime.Now.Millisecond.ToString());
                 Thread.Sleep(j);//讓線程暫停
             }
         }
     }
 }

對上面的代碼做一點說明,就是線程啓動之後,線程的實例不必再存在,例如在上面的代碼中我用的是同一個實例實例化了兩個線程,並且這兩個線程運行很正常。
 
 繼續探索
 上面解決了一個問題,如果在啓動線程時需要參數如何解決,如果針對上面的問題繼續發掘,比如:在啓動線程時不但要指定線程的暫停間隔,還需要指定循環次數(在上面的所有例子中都是執行10次的),這個問題該如何解決呢?
 有兩種辦法可以解決:
 首先可以繼續在ParameterizedThreadStart這裏做文章,因爲這裏可以使用一個Object類型的參數,那麼可以通過數組或者一個類來解決(因爲它們都是Object的子類)。我在做某個系統時確實採用數組處理過這種情況,這樣就要求在線程啓動方法中必須清楚知道數組中每個參數的用途,不是太方便。
 這裏說說重新定義一個實體類來解決的方法,代碼如下。

view plaincopy to clipboardprint?
using System;  
    using System.Collections.Generic;  
    using System.Text;  
    using System.Threading;  
      
    namespace StartThread  
    {  
        class MyThreadParameter  
        {  
            private int interval;  
            private int loopCount;  
            /// <summary>  
            /// 循環次數  
            /// </summary>  
            public int LoopCount  
            {  
                get { return loopCount; }  
            }  
          
            /// <summary>  
            /// 線程的暫停間隔  
            /// </summary>  
            public int Interval  
            {  
                get { return interval; }  
            }  
            /// <summary>  
            /// 構造函數  
            /// </summary>  
            /// <param name="interval">線程的暫停間隔</param>  
            /// <param name="loopCount">循環次數</param>  
            public MyThreadParameter(int interval,int loopCount)  
            {  
                this.interval = interval;  
                this.loopCount = loopCount;  
            }  
        }  
        class Program  
        {  
            int interval = 200;  
            static void Main(string[] args)  
            {  
                Program p = new Program();  
      
                Thread parameterThread = new Thread(new ParameterizedThreadStart(p.MyParameterRun));  
                parameterThread.Name = "Thread A:";  
                MyThreadParameter paramter = new MyThreadParameter(50, 20);  
                parameterThread.Start(paramter);  
            }  
       
      
            /// <summary>  
            /// 帶多個參數的啓動方法  
            /// </summary>  
            /// <param name="ms">方法參數</param>  
            public void MyParameterRun(object ms)  
            {  
                MyThreadParameter parameter = ms as MyThreadParameter;//類型轉換  
                if (parameter != null)  
                {  
                    for (int i = 0; i < parameter.LoopCount; i++)  
                    {  
                        Console.WriteLine(Thread.CurrentThread.Name + "系統當前時間毫秒值:" + DateTime.Now.Millisecond.ToString());  
                        Thread.Sleep(parameter.Interval);//讓線程暫停  
                    }  
                }  
            }  
        }  
    } 
using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Threading;
 
 namespace StartThread
 {
     class MyThreadParameter
     {
         private int interval;
         private int loopCount;
         /// <summary>
         /// 循環次數
         /// </summary>
         public int LoopCount
         {
             get { return loopCount; }
         }
  
         /// <summary>
         /// 線程的暫停間隔
         /// </summary>
         public int Interval
         {
             get { return interval; }
         }
      /// <summary>
      /// 構造函數
      /// </summary>
         /// <param name="interval">線程的暫停間隔</param>
         /// <param name="loopCount">循環次數</param>
         public MyThreadParameter(int interval,int loopCount)
         {
             this.interval = interval;
             this.loopCount = loopCount;
         }
     }
     class Program
     {
         int interval = 200;
         static void Main(string[] args)
         {
             Program p = new Program();
 
             Thread parameterThread = new Thread(new ParameterizedThreadStart(p.MyParameterRun));
             parameterThread.Name = "Thread A:";
             MyThreadParameter paramter = new MyThreadParameter(50, 20);
             parameterThread.Start(paramter);
         }
 
 
         /// <summary>
         /// 帶多個參數的啓動方法
         /// </summary>
         /// <param name="ms">方法參數</param>
         public void MyParameterRun(object ms)
         {
             MyThreadParameter parameter = ms as MyThreadParameter;//類型轉換
             if (parameter != null)
             {
                 for (int i = 0; i < parameter.LoopCount; i++)
                 {
                     Console.WriteLine(Thread.CurrentThread.Name + "系統當前時間毫秒值:" + DateTime.Now.Millisecond.ToString());
                     Thread.Sleep(parameter.Interval);//讓線程暫停
                 }
             }
         }
     }
 }

第二種方法和上面方法有些相似,也是需要引入外部類,並且將Thread實例放在引入的類中,這種情況適合於在線程中處理的業務邏輯比較複雜的情況。在前不久處理的一個項目中我用過這種情況,它是用來實現雙向數據傳輸的。
 如果實現上面的效果,代碼如下:

view plaincopy to clipboardprint?
using System;  
    using System.Collections.Generic;  
    using System.Text;  
    using System.Threading;  
      
    namespace StartThread  
    {  
        class MyThreadParameter  
        {  
            private int interval;  
            private int loopCount;  
            private Thread thread;  
              
        /// <summary>  
        /// 構造函數  
        /// </summary>  
            /// <param name="interval">線程的暫停間隔</param>  
            /// <param name="loopCount">循環次數</param>  
            public MyThreadParameter(int interval,int loopCount)  
            {  
                this.interval = interval;  
                this.loopCount = loopCount;  
                thread = new Thread(new ThreadStart(Run));  
            }  
      
            public void Start()  
            {  
                if (thread != null)  
                {  
                    thread.Start();  
                }  
            }  
      
            private void Run()  
            {  
                for (int i = 0; i < loopCount; i++)  
                {  
                    Console.WriteLine("系統當前時間毫秒值:" + DateTime.Now.Millisecond.ToString());  
                    Thread.Sleep(interval);//讓線程暫停  
                }  
            }  
        }  
        class Program  
        {  
            static void Main(string[] args)  
            {  
                MyThreadParameter parameterThread = new MyThreadParameter(30, 50);  
                parameterThread.Start();  
            }  
      
        }  
    } 
using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Threading;
 
 namespace StartThread
 {
     class MyThreadParameter
     {
         private int interval;
         private int loopCount;
         private Thread thread;
        
  /// <summary>
  /// 構造函數
  /// </summary>
         /// <param name="interval">線程的暫停間隔</param>
         /// <param name="loopCount">循環次數</param>
         public MyThreadParameter(int interval,int loopCount)
         {
             this.interval = interval;
             this.loopCount = loopCount;
             thread = new Thread(new ThreadStart(Run));
         }
 
         public void Start()
         {
             if (thread != null)
             {
                 thread.Start();
             }
         }
 
         private void Run()
         {
             for (int i = 0; i < loopCount; i++)
             {
                 Console.WriteLine("系統當前時間毫秒值:" + DateTime.Now.Millisecond.ToString());
                 Thread.Sleep(interval);//讓線程暫停
             }
         }
     }
     class Program
     {
         static void Main(string[] args)
         {
             MyThreadParameter parameterThread = new MyThreadParameter(30, 50);
             parameterThread.Start();
         }
 
     }
 }

上面的代碼的運行效果和前面的代碼運行效果類似,只不過是將業務處理代碼放在一個單獨的類MyThreadParameter中,使得MyThreadParameter看起來也像一個Thread,實際上維護的還是其內部的Thread,在一些大型系統中這樣做的好處是便於維護。
 
 總結:在本篇主要講述如何啓動線程的問題,在啓動時可能會遇到無需參數、需要多個參數的情況,在這裏講述瞭如何解決這些問題的思路。在.net類庫中雖然存在着龐大的類庫,但是並不是總會有合適的類來解決我們所遇到的問題,但是隻要肯動腦筋總會想到合適的辦法。

 

本文來自CSDN博客出處:http://blog.csdn.net/zhoufoxcn/archive/2009/08/03/4402999.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章