網(wǎng)站建設(shè)與管理教學(xué)計(jì)劃長(zhǎng)沙網(wǎng)站se0推廣優(yōu)化公司
.Net 中的同步上下文
【文 / 張賜榮】
什么是同步上下文?
同步上下文(SynchronizationContext)是一個(gè)抽象類,它提供了一個(gè)基本的功能,用于在不同的同步模型中傳播一個(gè)同步操作。 同步上下文表示一個(gè)代碼執(zhí)行的位置,它可以將傳遞給它的Send或Post方法的委托調(diào)用到該位置。 Send方法是同步的,Post方法是異步的。
每個(gè)線程都可以有一個(gè)與之關(guān)聯(lián)的同步上下文實(shí)例。可以通過(guò)調(diào)用靜態(tài)方法SynchronizationContext.SetSynchronizationContext來(lái)將運(yùn)行線程與一個(gè)同步上下文關(guān)聯(lián),也可以通過(guò)SynchronizationContext.Current屬性來(lái)查詢運(yùn)行線程的當(dāng)前同步上下文。
同步上下文并不一定代表一個(gè)特定的線程,它也可以將委托的調(diào)用轉(zhuǎn)發(fā)到任何多個(gè)線程(例如,到一個(gè)線程池工作線程),或者(至少理論上)到一個(gè)特定的CPU核心,甚至到另一個(gè)網(wǎng)絡(luò)主機(jī)。委托最終運(yùn)行在哪里取決于使用的同步上下文的類型。
同步上下文的作用
同步上下文的目的是讓公共語(yǔ)言運(yùn)行時(shí)(CLR)的內(nèi)部異步/同步操作能夠在不同的同步模型中正確地工作。 這個(gè)模型也簡(jiǎn)化了托管應(yīng)用程序在不同的同步環(huán)境中工作時(shí)必須遵循的一些要求。
例如,在Windows Forms應(yīng)用程序中,只有一個(gè)主UI線程,它負(fù)責(zé)創(chuàng)建和更新窗體和控件。如果在其他線程上直接訪問(wèn)UI元素,就會(huì)導(dǎo)致異?;虿灰恢碌男袨?。因此,Windows Forms會(huì)在創(chuàng)建首個(gè)窗體的線程上安裝一個(gè)WindowsFormsSynchronizationContext,它會(huì)將委托放到UI線程上執(zhí)行。 這樣,就可以在其他線程上異步地執(zhí)行一些耗時(shí)或阻塞的操作,然后通過(guò)Post方法將結(jié)果傳遞給UI線程同步執(zhí)行更新界面的操作。
同步上下文和異步編程
.NET提供了一些異步編程模式和語(yǔ)法糖,例如async/await、Task、TaskCompletionSource等,它們都依賴于同步上下文來(lái)實(shí)現(xiàn)正確和高效地異步操作。
當(dāng)使用async/await編寫(xiě)異步代碼時(shí),編譯器會(huì)將方法分割成兩部分:第一部分是從方法開(kāi)始到第一個(gè)await表達(dá)式之前的代碼;第二部分是從第一個(gè)await表達(dá)式之后到方法結(jié)束的代碼。第二部分被包裝成一個(gè)委托,并傳遞給第一個(gè)await表達(dá)式所等待的任務(wù)(Task)作為其完成時(shí)要執(zhí)行的續(xù)發(fā)。
當(dāng)任務(wù)完成時(shí),它會(huì)檢查當(dāng)前線程的同步上下文,并將委托調(diào)用到該同步上下文上。 這樣,就可以保證異步續(xù)發(fā)在與原始方法相同的位置執(zhí)行,例如在UI線程或請(qǐng)求線程上。這對(duì)于訪問(wèn)一些需要特定線程或上下文的資源是很有用的。
但是,有時(shí)候我們并不需要異步續(xù)發(fā)在原始方法的位置執(zhí)行,而是希望它在默認(rèn)的線程池上執(zhí)行,以提高性能和并發(fā)度。這時(shí)候,我們可以使用ConfigureAwait方法來(lái)指定是否要捕獲當(dāng)前的同步上下文。例如:
var task = DoNavigationAsync ();
await task.ConfigureAwait (false); // 不捕獲當(dāng)前的同步上下文
MessageBox.Show (\"Navigation done!\"); // 可能在其他線程上執(zhí)行
這樣,異步續(xù)發(fā)就不會(huì)使用當(dāng)前的同步上下文,而是使用默認(rèn)的任務(wù)調(diào)度器(TaskScheduler.Default),它會(huì)將委托調(diào)用到一個(gè)線程池工作線程上。 這樣做的好處是可以避免一些潛在的死鎖或性能問(wèn)題。
另一種異步編程模式是使用Task和TaskCompletionSource來(lái)創(chuàng)建和完成任務(wù)。Task表示一個(gè)異步操作的結(jié)果,它可以有三種狀態(tài):未完成、成功完成或失敗完成。TaskCompletionSource是一個(gè)包裝了一個(gè)任務(wù)的類,它提供了一種設(shè)置任務(wù)狀態(tài)和結(jié)果的方法。
當(dāng)我們使用TaskCompletionSource創(chuàng)建一個(gè)任務(wù)時(shí),我們可以指定一個(gè)任務(wù)創(chuàng)建選項(xiàng)(TaskCreationOptions),其中有一個(gè)選項(xiàng)是HideScheduler,它表示不要從當(dāng)前環(huán)境中獲取任務(wù)調(diào)度器。 這樣,當(dāng)我們使用ContinueWith方法為任務(wù)添加續(xù)發(fā)時(shí),就不會(huì)使用當(dāng)前的同步上下文,而是使用默認(rèn)的任務(wù)調(diào)度器。例如:
var tcs = new TaskCompletionSource<int> (TaskCreationOptions.HideScheduler);
var task = tcs.Task;
task.ContinueWith (t => Console.WriteLine (t.Result)); // 在線程池上執(zhí)行
tcs.SetResult (42); // 完成任務(wù)
如果我們不指定HideScheduler選項(xiàng),那么ContinueWith方法就會(huì)使用當(dāng)前的同步上下文(如果存在)來(lái)執(zhí)行續(xù)發(fā)。
總結(jié)
同步上下文是.NET中一個(gè)重要的概念,它影響了異步編程模式和語(yǔ)法的行為和效果。不同的.NET平臺(tái)和環(huán)境實(shí)現(xiàn)了不同的同步上下文,以適應(yīng)不同的同步模型。理解同步上下文的作用和用法,可以幫助我們編寫(xiě)正確和高效的異步代碼。
?