在多線程程式中經常會碰到線程同步:
情境1
主線程啟動多個子線程後,主線程需要等待所有的子線程執行完畢後,主線程才能進一步向下執行。
C# 提供了 ManualResetEvent 類為我們的線程同步提供了方便.
ManualResetEvent.WaitAll(new WaitHandle[] { });
WaitAll靜態方法提供了阻塞當前線程的執行,直到WaitHandle[]中的每個線程發送了阻塞解除的訊號,當前線程才會繼續執行。
可以通過將布爾值傳遞給建構函式來控制 ManualResetEvent 的初始狀態,如果初始狀態處於終止狀態,為 true;否則為 false。
ManualResetEvent.Set() 方法發送阻塞終止的訊號,當其它線程收到線程阻塞解除的訊號,就會繼續執行。
情境:主線程需要對子線程1產生的資料與子線程2產生的資料求和,主線程啟動線程1等待產生資料1,啟動線程2等待產生資料2,只有當
資料1與資料2產生後,主線程才能對資料求和。
代碼如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Diagnostics;namespace ThreadStudy.Hbb0b0{ /// <summary> /// 本案例說明主線程如何等待輔助線程都執行完畢之後繼續執行。 /// 主線程啟動輔助線程1產生加數1,啟動輔助線程2產生加數2 /// 主線程等待輔助線程1與輔助線程2的產生的加數之後,對加數 /// 進行累加,並輸出結果。 /// </summary> class Program { delegate int operateNumber(int a, int b); delegate int generateNumber(int a); static int result1; static int result2; static void Main(string[] args) { //輔助線程1阻塞標誌 ManualResetEvent m1 = new ManualResetEvent(false); //輔助線程2的阻塞標誌 ManualResetEvent m2 = new ManualResetEvent(false); generateNumber g1 = ThreadTool.GenerateNumber; generateNumber g2 = ThreadTool.GenerateNumber; AsyncCallback g1_callback = delegate(IAsyncResult ar) { result1 = g1.EndInvoke(ar); Console.WriteLine("thread id:{0} current number:{1} block stop", Thread.CurrentThread.ManagedThreadId, result1); //輔助線程阻塞完畢 m1.Set(); }; AsyncCallback g2_callback = delegate(IAsyncResult ar) { result2 = g2.EndInvoke(ar); Console.WriteLine("thread id:{0} current number:{1} block stop", Thread.CurrentThread.ManagedThreadId, result2); //輔助線程阻塞完畢 m2.Set(); }; Console.WriteLine("generate numbers:"); //輔助線程1啟動 g1.BeginInvoke(10, g1_callback, null); //輔助線程2啟動 g2.BeginInvoke(100, g2_callback, null); //主線線程等待所有輔助線程迴歸 ManualResetEvent.WaitAll(new WaitHandle[] { m1, m2 }); int sum= ThreadTool.sum(result1, result2); Console.WriteLine("{0}+{1}={2}", result1, result2, sum); Console.Read(); } } public class ThreadTool { /// <summary> /// 產生數字 /// </summary> /// <param name="maxNum"></param> /// <returns></returns> static public int GenerateNumber(int maxNum) { Console.WriteLine("thread Id:{0} generate number", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(5000); int result = new Random((int)DateTime.Now.Ticks).Next(maxNum); return result; } /// <summary> /// 累加結果 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> static public int sum(int a, int b) { Console.WriteLine("thread Id:{0} gernerate number", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(6000); return a + b; } }}
情境2 :
主線程啟動子線程1,子線程2, 只要子線程1或子線程2 任一線程執行完畢,主線程就可以繼續執行。
代碼如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;namespace ThreadWaitOne{ class ThreadStartParameterInfo { /// <summary> /// 線程訊號 /// </summary> public ManualResetEvent m_mr; /// <summary> /// 線程等待時間 /// </summary> public int m_waitTicks; public ThreadStartParameterInfo(ManualResetEvent mr,int waitTicks) { m_mr = mr; m_waitTicks = waitTicks; } } public class ThreadTool { private object m_SyncObject = new object(); private int currentThreadId = -1; public void Main() { for (int i = 0; i < 5; i++) { ManualResetEvent mr1 = new ManualResetEvent(false); ManualResetEvent mr2 = new ManualResetEvent(false); ManualResetEvent mr3 = new ManualResetEvent(false); ParameterizedThreadStart pts1 = new ParameterizedThreadStart(this.GenerateNumber); ParameterizedThreadStart pts2 = new ParameterizedThreadStart(this.GenerateNumber); ParameterizedThreadStart pts3 = new ParameterizedThreadStart(this.GenerateNumber); Thread ts1 = new Thread(pts1); Thread ts2 = new Thread(pts1); Thread ts3 = new Thread(pts1); ts1.Start (new ThreadStartParameterInfo(mr1, 3010)); ts2.Start(new ThreadStartParameterInfo(mr2, 3020)); ts3.Start(new ThreadStartParameterInfo(mr3, 3030)); //主線程等待任一線程,任一線程發送阻塞完成訊號後,主線程就可以繼續執行 ManualResetEvent.WaitAny(new WaitHandle[] { mr1, mr2, mr3 }); Console.WriteLine("Main Thread: The fast thread:{0}", currentThreadId); } } public void GenerateNumber(object resetEvent) { ThreadStartParameterInfo threadInfo = resetEvent as ThreadStartParameterInfo; //ManualResetEvent mr = resetEvent as ManualResetEvent; Console.WriteLine("thread Id:{0} wait:{1} generate number start:.....", Thread.CurrentThread.ManagedThreadId,threadInfo.m_waitTicks); Thread.Sleep(threadInfo.m_waitTicks); int radomNumber = new Random((int)DateTime.Now.Ticks).Next(1000); lock(m_SyncObject) { currentThreadId = Thread.CurrentThread.ManagedThreadId; } Console.WriteLine("thread Id:{0} generate number:{1}", Thread.CurrentThread.ManagedThreadId, radomNumber); threadInfo.m_mr.Set(); } }}