【總結】C# 線程同步技術(一)之 Join 方法


最近工作閑暇之際,翻閱了以前保存的電子書《C#多線程編程手冊》,發現此書同步技術這塊寫的甚好,於是參考此書並結合實例,對同步技術做一下總結和分析,也算是讀書筆記與心得體會吧,並與大家分享。

書中提到的同步技術有很多種,歸納起來常用的方式有以下幾種:

1、利用屬性標簽方式進行方法同步和上下文同步:MethodImplAttribute 類SynchronizationAttribute 類
2、同步代碼區:Monitor 類Lock 關鍵字ReaderWriterLock 類
3、手控同步:AutoResetEvent 類ManualResetEvent 類Mutex 類Interlocked 類

后面的文章我們會依次對以上類或關鍵字進行介紹,首先我們先來說說Thread類中的Join方法,書中對它的介紹比較簡略,但是我覺得它也算是線程間同步方式的一種了,對它的用法也來總結和歸納一下。 

MSDN對 Join 方法解釋說:在繼續執行標准的 COM 和 SendMessage 消息泵處理期間,阻塞調用線程,直到某個線程終止或經過了指定時間為止。

哈哈,MSDN的解釋永遠都是那么的繞嘴,我們還是通過一個簡單的控制台程序作為例子來說明一下吧:

首先我們建立一個計算類Calculate,里面包含一個加法線程 ThreadAdd 和一個加法方法,在Add()方法中並讓執行運算的線程休眠5秒,代碼如下: 

//計算類
public class Calculate
    {
        public Thread threadAdd;
        
        public Calculate()
        {
            threadAdd = new Thread(new ThreadStart(Add));            
        }

        //加法運算
        public void Add()
        {
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine("進入加法計算"); 
            Thread.Sleep(5000); Console.ForegroundColor
= ConsoleColor.Blue; Console.WriteLine("加法運算結果: x={0} y={1} x+y={2}", 1, 2, 1 + 2); } }

 然后我們在Main方法中進行調用:

class Program
    {
        static void Main(string[] args)
        {
            Calculate calculate = new Calculate();
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("主線程輸出:准備進行加法運算:");
            calculate.threadAdd.Start();
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("主線程輸出:運算完畢");
            Console.ReadKey();
        }
    }

 運算結果如圖:

 到這里,我們會發現執行Main() 方法的主線程,並沒有等待 執行加法的工作線程,而是直接輸出了“運算完畢”,這時候我們的Join() 方法就該上場了,我們對Main() 函數進行修改一下:

 static void Main(string[] args)
        {
            Calculate calculate = new Calculate();
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("主線程輸出:准備進行加法運算:");
            calculate.threadAdd.Start();
            //增加Join
            calculate.threadAdd.Join();
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("主線程輸出:運算完畢");
            Console.ReadKey();
        }

 運行結果如下圖:

這樣的結果是我們想要的正常輸出順序。運行的時候我們發現,當主線程執行到 calculate.threadAdd.Join(); 的時候,並沒有繼續執行,一直等到 加法線程 運算完畢之后主線程才繼續運行,這不就是和MSDN中解釋的一樣嗎?主線程現在就屬於調用線程,當主線程調用了calculate.threadAdd.Join()的時候,就發生了阻塞,直到加法線程運行完畢之后,才繼續運行。

現在我們在來看看Join的另外兩個重載方法:Join(Int32) 和 Join(TimeSpan),這兩個方法其實是一樣的,輸入參數說白了就是設置阻塞的等待時間,返回值是bool類型,如果線程已終止,則為 true,否則返回 false 。不明白沒關系,我們繼續來看例子:

我們修改一計算類,再增加一個 減法方法Sub() 和一個執行減法的線程ThreadSub,代碼如下:

//計算類
    public class Calculate
    {
        public Thread threadAdd;
        public Thread threadSub;
        public Calculate()
        {
            threadAdd = new Thread(new ThreadStart(Add));
            threadSub = new Thread(new ThreadStart(Sub));
        }

        //加法運算
        public void Add()
        {
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine("進入加法計算");
            Thread.Sleep(5000);
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine("加法運算結果: x={0} y={1} x+y={2}", 1, 2, 1 + 2);
        }

        //新增減法運算 
        public void Sub()
        {
            //主要是這里
            bool b = threadAdd.Join(1000);
            Console.ForegroundColor = ConsoleColor.Red;
            if (b)
            {
                Console.WriteLine("加法運算已經完成,進入減法法計算");
            }
            else
            {
                Console.WriteLine("加法運算超時,先進入減法法計算");
            }

            Thread.Sleep(2000);
            Console.WriteLine("進入減法運算");
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("減法運算結果: x={0} y={1} x-y={2}", 10, 2, 10 - 2);
        }
    }

 Main() 方法修改為:

class Program
    {
        static void Main(string[] args)
        {
            Calculate calculate = new Calculate();
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("主線程輸出:准備進行加法和減法兩種運算:");

            calculate.threadAdd.Start();
            calculate.threadSub.Start();
            calculate.threadAdd.Join();
            calculate.threadSub.Join();

            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("主線程輸出:所有運算完畢");
            Console.ReadKey();
        }
    }

 運行結果如下:

結果是正確的,我們來分析一下整個的運算過程:

首先,主線程遇到 calculate.threadAdd.Join();  和 calculate.threadSub.Join(); 肯定會發生阻塞,等待這兩個線程完成后,才會繼續執行,這個不容質疑。然后我們看加法線程和減法線程,這兩個線程幾乎同時執行,誰先執行,我們是不可預期的。比如先執行加法線程,當執行到Thread.Sleep(5000),的時候,加法線程休眠5s,減法線程由於調用了 threadAdd.Join(1000); 所以減法線程會阻塞1s ,1s 之后由於加法線程還沒有執行完成,所以 返回值為 false,減法線程繼續執行,減法線程執行完畢后,又過了一會,加法線程才繼續執行。這樣就會得出我們上面的運行結果。

Thread.Join() 方法的用法這么多了,下一篇 來總結和說明一下  MethodImplAttribute 類 和 SynchronizationAttribute 類。

本 文源 碼 下 載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM