最近在項目中使用多線程,但是對多線程的一些用法和概念還有有些模稜兩可,為了搞清楚查閱了一寫資料,寫下這篇日志加深理解吧。
Thread.Join()在MSDN中的解釋很模糊:Blocks the calling thread until a thread terminates
有兩個主要問題:1.什麼是the calling thread?
2.什麼是a thread?
首先來看一下有關的概念: 我們執行一個.exe文件實際上就是開啟了一個進程,同時開啟了至少一個線程,
但是真正干活的是線程,就好比一個Team有好幾個人,但是真正干活的是人不是Team.
具體到代碼來說,以控制台程序為例:程序Test.exe從Main函數開始運行,實際上是有一個線程
在執行Main函數,我們稱作MainThread.假如我們在Main函數中聲明了一個Thread,稱作NewThread,並且調用了
NewThread.Start()的方法,那麼 MainThread在處理Main函數裡面的代碼時遇到NewThread.Start()時,就會
去調用NewThread.
基於上面的討論,我們可以得出結論:在我們剛才的例子中the calling thread就是MainThread,而a thread
指的洽洽就是MainThread調用的NewThread線程。
現在回到MSDN的解釋,我們可以這麼翻譯:當NewThread調用Join方法的時候,MainThread就被停止執行,
直到NewThread線程執行完畢。這樣就好理解了。
static void Main(string[] args) { Thread thread1 = new Thread(new ThreadStart(()=> { Thread.Sleep(500); Console.WriteLine("我是新線程打印的!"); })); thread1.Start(); Console.WriteLine("我是主線程打印的!"); Console.Read(); }
在Main函數中開啟一個新的線程執行NewFunc方法,在方法中先休息500毫秒然後打印一段標志語。雖然thread1.Start()先於主線程的打印語句,但是新線程休息了500毫秒,所以執行結果應該是:
從結果中可以看到,先執行的主線程,然後執行的新線程,如果我們想讓新線程執行完畢後再繼續執行主線程呢?這時就用到了Thread.Join(),我們在thread1.Start()後面添加thread1.Join(),這樣就會先執行完新線程後再去執行主線程。
static void Main(string[] args) { Thread thread1 = new Thread(new ThreadStart(()=> { Thread.Sleep(500); Console.WriteLine("我是新線程打印的!"); })); thread1.Start(); thread1.Join(); Console.WriteLine("我是主線程打印的!"); Console.Read(); }
這段代碼執行的結果為:
這次打印的結果和沒加thread1.Join()的輸出結果剛好相反。
到此我們可以得出結論,當調用Thread.Join()後,主線程是被阻塞了的,直到新線程執行完畢才繼續執行,這是可以肯定的,可是我們目前只開了一個線程,如果在開一個線程會怎麼樣呢?我們接著測試:
static void Main(string[] args) { Thread thread1 = new Thread(new ThreadStart(()=> { for (int i = 0; i < 10; i++) { Thread.Sleep(100); Console.WriteLine("我是第1個線程打印的!"); } })); Thread thread2 = new Thread(new ThreadStart(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(100); Console.WriteLine("我是第2個線程1打印的!"); } })); thread2.Start(); thread1.Start(); thread2.Join(); thread1.Join(); Console.WriteLine("我是主線程打印的!"); Console.Read(); }
輸出結果為:
雖然第二個線程在第一個線程剛剛啟動後就調用了Join()但是並沒有阻塞第一個線程的執行,由此可以驗證the calling thread,應為第二個線程是由主線程開啟的,所以只能阻塞主線程,而不能阻塞其他線程,下面再接著實驗在線程中再開一個新的線程:
static void Main(string[] args) { Thread thread1 = new Thread(new ThreadStart(()=> { Thread thread11 = new Thread(new ThreadStart(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(100); Console.WriteLine("我是第1-1個線程1打印的!"); } })); thread11.Start(); for (int i = 0; i < 10; i++) { Thread.Sleep(100); Console.WriteLine("我是第1個線程打印的!"); } })); thread1.Start(); Console.WriteLine("我是主線程打印的!"); Console.Read(); }
改代碼的執行結果是:
thread1和thread11是同步執行的,由於thread11是由thread1開啟的,下面調用thread11在看看結果:
static void Main(string[] args) { Thread thread1 = new Thread(new ThreadStart(()=> { Thread thread11 = new Thread(new ThreadStart(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(100); Console.WriteLine("我是第1-1個線程1打印的!"); } })); thread11.Start(); thread11.Join(); for (int i = 0; i < 10; i++) { Thread.Sleep(100); Console.WriteLine("我是第1個線程打印的!"); } })); thread1.Start(); Console.WriteLine("我是主線程打印的!"); Console.Read(); }
改代碼的執行結果為:
由此可見,thread11.Join()方法是阻塞了thread1的,並沒有阻塞主線程。再一次驗證the calling thread指的是開啟新線程的那個線程,而不一定是主線程。
此外,Thread.Join()還有兩個重載方法:
public bool Join(TimeSpan timeout);
public bool Join(int millisecondsTimeout);
兩個方法的參數不一樣但是效果是一樣的,目的是阻塞the calling thread的一定的時間,如果過了這個時間子線程還沒有執行完畢,那麼the calling thread就會接著執行。例如:我中午叫個同事一起去吃飯,但是他手頭還有點工作磨磨唧唧的一直沒有做完,如果是Join()的話我就一直等著,直到他做完我倆一起去吃飯,而Join(TimeSpan timeout)和Join(int millisecondsTimeout)就是,你Y快點啊,我在等你幾分鐘,你在默默唧唧干不完我就不等你了,我先去了。