SynchronizationContext在通訊中充當傳輸者的角色,實現功能就是一個執行緒和另外一個執行緒的通訊。
需要注意的是,不是每個執行緒都附加SynchronizationContext這個物件,只有UI執行緒是一直擁有的。故獲取SynchronizationContext也只能在UI執行緒上進行SynchronizationContext context = SynchronizationContext.Current;
那什麼時候會用到呢?
在多執行緒操作時往往需要切回某個執行緒中去工作,等完成後再切回來。
如主UI執行緒中建立了一個子執行緒A。A中新增了委託事件。UI執行緒中向A執行緒的類註冊了事件,當A執行緒觸發事件時去修改UI上的屬性如TEXT。
這個時候往往要在UI執行緒向子執行緒註冊的事件方法中使用控制元件的invoke方法才能訪問UI執行緒中的控制元件,因為這些註冊的事件(委託)方法程式碼雖然看似寫在UI執行緒的Form類中,但實際上是註冊在了子執行緒A的事件中,它們是會被子執行緒A觸發事件時在子執行緒內部執行的。這樣,我們不得不在主UI執行緒的類的註冊事件方法中透過控制元件的Invoke方法才能訪問控制元件,這樣做十分麻煩。我們想和系統的控制元件事件一樣,直接在註冊的事件方法中訪問控制元件。那麼這個時候就可以用SynchronizationContext了。
SynchronizationContext.Send(SendOrPostCallback d,object state);
SynchronizationContext.Post(SendOrPostCallback d,object state);
d 為一個沒有返回值,並且具有一個Object型別傳入引數的委託(SendOrPostCallback );
state 為執行這個委託時的引數(object);
注意:
SynchronizationContext的物件不是所有執行緒都被附加的,只有UI主執行緒會被附加。
對於UI執行緒來說,是如何將SynchronizationContext這個物件附加到執行緒上的呢?
在Form1 form = new Form1()之前,SynchronizationContext物件是為空,而當例項化Form1窗體後,SynchronizationContext物件就被附加到這個執行緒上了。
所以可以得出答案了:當Control物件被建立的同時,SynchronizationContext物件也會被建立並附加到執行緒上。所以在使用時,一定要等窗體InitializeComponent(); 這個完成後 它才能得到一個不是NULL的物件.
那麼SynchronizationContext的Send()和Post()二個方法有什麼區別呢?
Send() 是簡單的在當前執行緒上去呼叫委託來實現(同步呼叫)。也就是在子執行緒上直接呼叫UI執行緒執行,等UI執行緒執行完成後子執行緒才繼續執行。
Post() 是線上程池上去呼叫委託來實現(非同步呼叫)。這是子執行緒會從執行緒池中找一個執行緒去調UI執行緒,子執行緒不等待UI執行緒的完成而直接執行自己下面的程式碼。
例子:
/// <summary> /// 這裡需要在主執行緒裡定義, /// 並在主執行緒獲得context = SynchronizationContext.Current /// </summary> private SynchronizationContext context; /// <summary> /// 窗體載入 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_Load(object sender, EventArgs e) { //此處就是之前提的在主執行緒獲得SynchronizationContext context = SynchronizationContext.Current; //之後可以開執行緒了 Thread thread = new Thread(new ThreadStart(Start)); thread.IsBackground = true; thread.Start(); } /// <summary> /// 執行緒操作 /// </summary> private void Start() { for(int i=0;i<100;++i) { //這邊即可正常呼叫主介面的控制元件了 context.Send(operation, i);//正確 //按原先直接應用,因為使用到控制元件會報錯 operation(i);//報錯 Thread.Sleep(100); } } /// <summary> /// 執行緒操作 /// </summary> /// <param name="obj"></param> private void operation(object obj) { textBox1.AppendText(obj.ToString() + "\r\n"); }