C#線(xiàn)程間通信的異步機(jī)制
線(xiàn)程間通信
我們看下面的圖
我們來(lái)看線(xiàn)程間通信的原理:線(xiàn)程(Thread B)和線(xiàn)程(Thread A)通信, 首先線(xiàn)程A 必須實(shí)現(xiàn)同步上下文對(duì)象(Synchronization Context), 線(xiàn)程B通過(guò)調(diào)用線(xiàn)程A的同步上下文對(duì)象來(lái)訪(fǎng)問(wèn)線(xiàn)程A,所有實(shí)現(xiàn)都是在同步上下文中完成的.線(xiàn)程B有兩種方式來(lái)實(shí)現(xiàn)線(xiàn)程間的通信。
第一種:調(diào)用線(xiàn)程A的同步上下文對(duì)象,阻礙當(dāng)前線(xiàn)程,執(zhí)行紅色箭頭調(diào)用,直到黃色箭頭返回(同步上下文執(zhí)行完畢)才釋放當(dāng)前線(xiàn)程. (1->2->3->5)。
第二種:調(diào)用線(xiàn)程A的同步上下文對(duì)象(實(shí)際上是在開(kāi)啟一個(gè)新線(xiàn)程去執(zhí)行,1->2->3->5) ,執(zhí)行紅色箭頭,但并不阻礙當(dāng)前線(xiàn)程(原有線(xiàn)程,1->4->5),綠色箭頭繼續(xù)執(zhí)行。
文章中將會(huì)通過(guò)下面幾個(gè)類(lèi)來(lái)進(jìn)行介紹:
- ISynchronizeInvoke 接口
- SynchronizationContext 類(lèi)
- AsyncOperation / AsyncOperationManager 類(lèi)
1. ISynchronizeInvoke 接口
我們先來(lái)看下面一段異步的代碼(Window Form控件下有1個(gè)Button/1個(gè)Label),但點(diǎn)擊Button的時(shí)候,執(zhí)行異步調(diào)用,完成后,告訴Window Form的 Label控件Text屬性”Asynchronous End”。
在windows應(yīng)用窗體應(yīng)用程序中,對(duì)窗體上控件屬性的任何修改都必須在主線(xiàn)程中完成。不能從其他線(xiàn)程安全地訪(fǎng)問(wèn)控件的方法和屬性。
ISynchronizeInvoke 接口來(lái)自.Net Framework 1.0,提供3個(gè)方法1個(gè)屬性:
- BeginInvoke / EndInvoke 方法 : 異步方法
- Invoke 方法 : 同步方法
- InvokeRequired 屬性 : 判讀來(lái)源的執(zhí)行線(xiàn)程
delegate void DoWork(); private void button1_Click(object sender, EventArgs e) { //更新?tīng)顟B(tài),添加到Listbox 中 AddValue("Asynchronous Start."); //使用委托來(lái)調(diào)用異步方法 DoWork work = DoWorkMethod; work.BeginInvoke(OnWorkCallback, work); } void OnWorkCallback(IAsyncResult asyncResult) { DoWork work = asyncResult.AsyncState as DoWork; if (work != null) { work.EndInvoke(asyncResult); } //(1)方法:調(diào)用Control控件的Invoke //Action<string> asyncUpdateState = UpdateStatus; //Action<string> 介紹=> 附1 //Invoke(asyncUpdateState, "1:Asynchronous End."); //(2)方法:直接在異步調(diào)用的線(xiàn)程下 UpdateStatus("2:Asynchronous End."); } void UpdateStatus(string input) { //把你需要通知的控件Control 賦值給ISynchronizeInvoke //來(lái)實(shí)現(xiàn)線(xiàn)程間的通信 ISynchronizeInvoke async = this.listBoxStatus; //使用(1)方法,InvokeRequired == false ,來(lái)源當(dāng)前(Window Form)主線(xiàn)程 if (async.InvokeRequired == false) AddValue(input); else// 使用(2)方法 == true ,來(lái)源其他線(xiàn)程(異步) { Action<string> action = new Action<string>(status => { AddValue(status); }); //調(diào)用ISynchronizeInvoke 提供的Invoke 同步方法,阻礙線(xiàn)程,直到調(diào)用結(jié)束 //也可以使用ISynchronizeInvoke 提供的異步BeginInvoke/EndInvoke方法來(lái)實(shí)現(xiàn)調(diào)用. async.Invoke(action, new object[] { input }); } } void AddValue(string input) { this.listBoxStatus.Items.Add(string.Format("[(#{2}){0}]Context is null:{1}", input, Thread.CurrentContext == null, Thread.CurrentThread.ManagedThreadId)); } void DoWorkMethod() { Thread.Sleep(3000);//模擬耗時(shí)工作 }
在代碼中(UpdateStatus方法體內(nèi)),我們可以看到主要是在ISynchronizeInvoke async = this.listBoxStatus;實(shí)現(xiàn)了線(xiàn)程間的通信,MSDN的解釋” 實(shí)現(xiàn)此接口的對(duì)象可以接收事件已發(fā)生的通知,并且可以響應(yīng)有關(guān)該事件的查詢(xún)”. 并使Window Form(主線(xiàn)程) 下的ListBox 控件和來(lái)自異步方法(另外一個(gè)線(xiàn)程)的建立了通道。
InvokeRequired 判斷線(xiàn)程的來(lái)源。
如果使用(1)方法,來(lái)源于Window Form 自身Control 的Invoke方法, InvokeRequired將返回false; 來(lái)源另外線(xiàn)程(異步)。
如果使用(2)返回true.同時(shí)ISynchronizeInvoke 提供了異步(BeginInvoke+EndInvok)和同步方法(Invoke)來(lái)實(shí)現(xiàn)線(xiàn)程間通信.Invoke 就是最上面的圖1 所示的第一種 / BeginInvoke+EndInvok 是第二種。
2. SynchronizationContext 類(lèi)
相比ISynchronizeInvoke 接口,SynchronizationContext 類(lèi)(來(lái)自.Net Framework 2.0)提供了更多的方法來(lái)操作同步上下文對(duì)象,實(shí)現(xiàn)線(xiàn)程間通信.在上面的例子中SynchronizationContext類(lèi)中將由 Post/Send 方法來(lái)實(shí)現(xiàn)。
反編譯后我們看到:
public virtual void Post(SendOrPostCallback d, object state) { ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state); } public virtual void Send(SendOrPostCallback d, object state) { d(state); }
- Send = ISynchronizeInvoke 中的Invoke 同步調(diào)用.圖1中的第一種
- Post = ISynchronizeInvoke 中的BeginInvoke + EndInvoke異步調(diào)用. 圖1中的第二種
SynchronizationContext 類(lèi)舉例(在WinForm 下編程)
delegate void DoWork(); private void button1_Click(object sender, EventArgs e) { //System.Windows.Forms.Form 自動(dòng)的創(chuàng)建默認(rèn)的同步上下文對(duì)象, //直接的獲取當(dāng)前的同步上下文對(duì)象 SynchronizationContext context = SynchronizationContext.Current; //更新?tīng)顟B(tài),添加到Listbox 中 AddValue<string>("Asynchronous Start."); //使用委托來(lái)調(diào)用異步方法 DoWork work = DoWorkMethod; work.BeginInvoke(OnWorkCallback, context); } void OnWorkCallback(IAsyncResult asyncResult) { AsyncResult async = (AsyncResult)asyncResult; DoWork work = (DoWork)async.AsyncDelegate; work.EndInvoke(asyncResult); //更新?tīng)顟B(tài) UpdateStatus("Asynchronous End.", asyncResult.AsyncState); } void UpdateStatus(object input,object syncContext) { //獲取主線(xiàn)程(Window Form)中同步上下文對(duì)象 SynchronizationContext context = syncContext as SynchronizationContext; //使用SynchronizationContext 類(lèi)中異步Post 方法 SendOrPostCallback callback = new SendOrPostCallback(p => { AddValue<object>(p); }); context.Post(callback, input);//Post 為異步,Send 為同步 } void AddValue<T>(T input) { this.listBoxStatus.Items.Add(string.Format("[(#{2}){0}]Context is null:{1}", input, Thread.CurrentContext == null, Thread.CurrentThread.ManagedThreadId)); } void DoWorkMethod() { Thread.Sleep(3000);//模擬耗時(shí)工作 }
上面我們已經(jīng)說(shuō)過(guò)在主線(xiàn)程中System.Windows.Forms.Form 自動(dòng)的創(chuàng)建默認(rèn)的同步上下文對(duì)象, 這時(shí)候我們把當(dāng)前的同步上下文對(duì)象通過(guò)參數(shù)的形式賦值到異步線(xiàn)程中,調(diào)用Post 方法來(lái)實(shí)現(xiàn), Post 方法接收 SendOrPostCallback 委托和額外object state參數(shù),在Post方法體內(nèi)調(diào)用線(xiàn)程池的線(xiàn)程來(lái)實(shí)現(xiàn)(Code2.1).當(dāng)然我們也可以直接使用Send方法。
下面我們看看線(xiàn)程中的代碼(在Console 下編程)。
static class Program { static void Main() { Output("Main Thread Start."); //為主線(xiàn)程創(chuàng)建Synchronization Context var context = new SynchronizationContext(); //開(kāi)始一個(gè)新線(xiàn)程 Thread threadB = new Thread(work); threadB.Start(context); Console.Read(); } static void work(object context) { Output("Thread B"); //獲取主線(xiàn)程中的同步上下文對(duì)象 SynchronizationContext sc = context as SynchronizationContext; //異步的方式和主線(xiàn)程通信,并發(fā)送"Hello World". sc.Post(new SendOrPostCallback(p => { Output(p); }), "Hello World"); } static void Output(object value) { Console.WriteLine("[ThreadID:#{0}]{1}", Thread.CurrentThread.ManagedThreadId, value); } }
在主線(xiàn)程中因?yàn)闆](méi)有同步上下文對(duì)象,所以開(kāi)始我們new SynchronizationContext(); 對(duì)象,其他和上面基本一樣.此例說(shuō)明了Post 是開(kāi)啟新線(xiàn)程(線(xiàn)程池)運(yùn)行的。
3. AsyncOperation / AsyncOperationManager 類(lèi)
AsyncOperation / AsyncOperationManager 類(lèi)是SynchronizationContext 類(lèi)的進(jìn)一步封裝和實(shí)現(xiàn), AsyncOperationManager在創(chuàng)建AsyncOperation對(duì)象的時(shí)候會(huì)取得當(dāng)前線(xiàn)程的同步上下文對(duì)象,并存儲(chǔ)在AsyncOperation之中,使我們?cè)L問(wèn)同步上下文對(duì)象更加容易。
public class MySynchronizedClass { private AsyncOperation operation; public event EventHandler somethingHappened; public MySynchronizedClass() { //創(chuàng)建AsyncOperation 對(duì)象,并把當(dāng)前線(xiàn)程的同步上下文保持到AsyncOperation中. operation = AsyncOperationManager.CreateOperation(null); Thread workerThread = new Thread(new ThreadStart(DoWork)); workerThread.Start(); } private void DoWork() { SendOrPostCallback callback = new SendOrPostCallback(state => { EventHandler handler = somethingHappened; if (handler != null) { handler(this, EventArgs.Empty); } }); operation.Post(callback, null); //注意1 operation.OperationCompleted(); } }
AsyncOperation類(lèi)中實(shí)現(xiàn)了OperationCompleted的方法. SynchronizationContext 類(lèi)中這個(gè)方法是沒(méi)有具體的代碼實(shí)現(xiàn)的。
到此這篇關(guān)于C#線(xiàn)程間異步通信的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#中Task.ContinueWith連續(xù)任務(wù)使用實(shí)例
本文主要介紹了C#中Task.ContinueWith連續(xù)任務(wù)使用實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02c#高效的線(xiàn)程安全隊(duì)列ConcurrentQueue<T>的實(shí)現(xiàn)
這篇文章主要介紹了c#高效的線(xiàn)程安全隊(duì)列ConcurrentQueue<T>的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11C#開(kāi)發(fā)微信門(mén)戶(hù)及應(yīng)用(2) 微信消息處理和應(yīng)答
文章主要為大家詳細(xì)介紹了C#開(kāi)發(fā)微信門(mén)戶(hù)及應(yīng)用第二篇,微信消息處理和應(yīng)答,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06C# 如何調(diào)用C++ dll string類(lèi)型返回
這篇文章主要介紹了C# 如何調(diào)用C++ dll string類(lèi)型返回問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11在C#中g(shù)lobal關(guān)鍵字的作用及其用法
global 是 C# 2.0 中新增的關(guān)鍵字,理論上說(shuō),如果代碼寫(xiě)得好的話(huà),根本不需要用到它,但是不排除一些特別的情況,比如修改別人的代碼,本文僅舉例說(shuō)明。2016-03-03C#使用CryptoStream類(lèi)加密和解密字符串的實(shí)現(xiàn)
CryptoStream設(shè)計(jì)用于在內(nèi)容以流的形式輸出到文件時(shí)加密和解密內(nèi)容,本文主要介紹了C#使用CryptoStream類(lèi)加密和解密字符串的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01C#制作鷹眼的詳細(xì)全過(guò)程(帶注釋)實(shí)例代碼
C#制作鷹眼的詳細(xì)全過(guò)程(帶注釋)實(shí)例代碼,需要的朋友可以參考一下2013-03-03