日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】1.2异步编程:使用线程池管理线程

發(fā)布時(shí)間:2023/12/10 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】1.2异步编程:使用线程池管理线程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

???????? 從此圖中我們會(huì)發(fā)現(xiàn) .NET 與C# 的每個(gè)版本發(fā)布都是有一個(gè)“主題”。即:C#1.0托管代碼→C#2.0泛型→C#3.0LINQ→C#4.0動(dòng)態(tài)語言→C#5.0異步編程?,F(xiàn)在我為最新版本的“異步編程”主題寫系列分享,期待你的查看及點(diǎn)評(píng)。

?

?

傳送門:異步編程系列目錄……

?

?

開始《異步編程:使用線程池管理線程》

示例程序:異步編程:使用線程池管理線程.rar

??????? 如今的應(yīng)用程序越來越復(fù)雜,我們常常需要使用《異步編程:線程概述及使用》中提到的多線程技術(shù)來提高應(yīng)用程序的響應(yīng)速度。這時(shí)我們頻繁的創(chuàng)建和銷毀線程來讓應(yīng)用程序快速響應(yīng)操作,這頻繁的創(chuàng)建和銷毀無疑會(huì)降低應(yīng)用程序性能,我們可以引入緩存機(jī)制解決這個(gè)問題,此緩存機(jī)制需要解決如:緩存的大小問題、排隊(duì)執(zhí)行任務(wù)、調(diào)度空閑線程、按需創(chuàng)建新線程及銷毀多余空閑線程……如今微軟已經(jīng)為我們提供了現(xiàn)成的緩存機(jī)制:線程池

???????? 線程池原自于對(duì)象池,在詳細(xì)解說明線程池前讓我們先來了解下何為對(duì)象池。

?

對(duì)象池

在系統(tǒng)設(shè)計(jì)中,我們嘗嘗會(huì)使用到“池”的概念。Eg:數(shù)據(jù)庫(kù)連接池,socket連接池,線程池,組件隊(duì)列?!俺亍笨梢怨?jié)省對(duì)象重復(fù)創(chuàng)建和初始化所耗費(fèi)的時(shí)間。對(duì)那些被系統(tǒng)頻繁請(qǐng)求和使用的對(duì)象,使用此機(jī)制可以提高系統(tǒng)運(yùn)行性能。

“池”是一種“以空間換時(shí)間”的做法,我們?cè)趦?nèi)存中保存一系列整裝待命的對(duì)象,供人隨時(shí)差遣。與系統(tǒng)效率相比,這些對(duì)象所占用的內(nèi)存空間太微不足道了。

?

流程圖:

?

???????? 對(duì)于對(duì)象池的清理通常設(shè)計(jì)兩種方式:

1)???????? 手動(dòng)清理,即主動(dòng)調(diào)用清理的方法。

2)???????? 自動(dòng)清理,即通過System.Threading.Timer來實(shí)現(xiàn)定時(shí)清理。

?

關(guān)鍵實(shí)現(xiàn)代碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public?sealed?class?ObjectPool<T> where?T : ICacheObjectProxy<T> { ????// 最大容量 ????private?Int32 m_maxPoolCount = 30; ????// 最小容量 ????private?Int32 m_minPoolCount = 5; ????// 已存容量 ????private?Int32 m_currentCount; ????// 空閑+被用 對(duì)象列表 ????private?Hashtable m_listObjects; ????// 最大空閑時(shí)間 ????private?int?maxIdleTime = 120; ????// 定時(shí)清理對(duì)象池對(duì)象 ????private?Timer timer = null; ? ????/// <summary> ????/// 創(chuàng)建對(duì)象池 ????/// </summary> ????/// <param name="maxPoolCount">最小容量</param> ????/// <param name="minPoolCount">最大容量</param> ????/// <param name="create_params">待創(chuàng)建的實(shí)際對(duì)象的參數(shù)</param> ????public?ObjectPool(Int32 maxPoolCount, Int32 minPoolCount, Object[] create_params){ } ? ????/// <summary> ????/// 獲取一個(gè)對(duì)象實(shí)例 ????/// </summary> ????/// <returns>返回內(nèi)部實(shí)際對(duì)象,若返回null則線程池已滿</returns> ????public?T GetOne(){ } ? ????/// <summary> ????/// 釋放該對(duì)象池 ????/// </summary> ????public?void?Dispose(){ } ? ????/// <summary> ????/// 將對(duì)象池中指定的對(duì)象重置并設(shè)置為空閑狀態(tài) ????/// </summary> ????public?void?ReturnOne(T obj){ } ? ????/// <summary> ????/// 手動(dòng)清理對(duì)象池 ????/// </summary> ????public?void?ManualReleaseObject(){ } ? ????/// <summary> ????/// 自動(dòng)清理對(duì)象池(對(duì)大于 最小容量 的空閑對(duì)象進(jìn)行釋放) ????/// </summary> ????private?void?AutoReleaseObject(Object obj){ } }

?

???????? 通過對(duì)“對(duì)象池”的一個(gè)大體認(rèn)識(shí)能幫我們更快理解線程池。

?

線程池ThreadPool類詳解

ThreadPool靜態(tài)類,為應(yīng)用程序提供一個(gè)由系統(tǒng)管理的輔助線程池,從而使您可以集中精力于應(yīng)用程序任務(wù)而不是線程管理。每個(gè)進(jìn)程都有一個(gè)線程池,一個(gè)Process中只能有一個(gè)實(shí)例,它在各個(gè)應(yīng)用程序域(AppDomain)是共享的。

在內(nèi)部,線程池將自己的線程劃分工作者線程(輔助線程)和I/O線程。前者用于執(zhí)行普通的操作,后者專用于異步IO,比如文件和網(wǎng)絡(luò)請(qǐng)求,注意,分類并不說明兩種線程本身有差別,內(nèi)部依然是一樣的。

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public?static?class?ThreadPool { ????// 將操作系統(tǒng)句柄綁定到System.Threading.ThreadPool。 ????public?static?bool?BindHandle(SafeHandle osHandle); ? ????// 檢索由ThreadPool.GetMaxThreads(Int32,Int32)方法返回的最大線程池線程數(shù)和當(dāng)前活動(dòng)線程數(shù)之間的差值。 ????public?static?void?GetAvailableThreads(out?int?workerThreads ????????????, out?int?completionPortThreads); ? ????// 設(shè)置和檢索可以同時(shí)處于活動(dòng)狀態(tài)的線程池請(qǐng)求的數(shù)目。 ????// 所有大于此數(shù)目的請(qǐng)求將保持排隊(duì)狀態(tài),直到線程池線程變?yōu)榭捎谩?/span> ????public?static?bool?SetMaxThreads(int?workerThreads, int?completionPortThreads); ????public?static?void?GetMaxThreads(out?int?workerThreads, out?int?completionPortThreads); ????// 設(shè)置和檢索線程池在新請(qǐng)求預(yù)測(cè)中維護(hù)的空閑線程數(shù)。 ????public?static?bool?SetMinThreads(int?workerThreads, int?completionPortThreads); ????public?static?void?GetMinThreads(out?int?workerThreads, out?int?completionPortThreads); ? ????// 將方法排入隊(duì)列以便執(zhí)行,并指定包含該方法所用數(shù)據(jù)的對(duì)象。此方法在有線程池線程變得可用時(shí)執(zhí)行。 ????public?static?bool?QueueUserWorkItem(WaitCallback callBack, object?state); ????// 將重疊的 I/O 操作排隊(duì)以便執(zhí)行。如果成功地將此操作排隊(duì)到 I/O 完成端口,則為 true;否則為 false。 ????// 參數(shù)overlapped:要排隊(duì)的System.Threading.NativeOverlapped結(jié)構(gòu)。 ????public?static?bool?UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped); ????// 將指定的委托排隊(duì)到線程池,但不會(huì)將調(diào)用堆棧傳播到工作者線程。 ????public?static?bool?UnsafeQueueUserWorkItem(WaitCallback callBack, object?state); ? ????// 注冊(cè)一個(gè)等待Threading.WaitHandle的委托,并指定一個(gè) 32 位有符號(hào)整數(shù)來表示超時(shí)值(以毫秒為單位)。 ????// executeOnlyOnce如果為 true,表示在調(diào)用了委托后,線程將不再在waitObject參數(shù)上等待; ????// 如果為 false,表示每次完成等待操作后都重置計(jì)時(shí)器,直到注銷等待。 ????public?static?RegisteredWaitHandle RegisterWaitForSingleObject( ????????????WaitHandle waitObject ????????????, WaitOrTimerCallback callBack, object?state, ????????????Int millisecondsTimeOutInterval, bool?executeOnlyOnce); ????public?static?RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( ??????????????WaitHandle waitObject ????????????, WaitOrTimerCallback callBack ????????????, object?state ????????????, int?millisecondsTimeOutInterval ????????????, bool?executeOnlyOnce); ????…… }

1.???????? 線程池線程數(shù)

1)???????? 使用GetMaxThreads()和SetMaxThreads()獲取和設(shè)置最大線程數(shù)

可排隊(duì)到線程池的操作數(shù)僅受內(nèi)存的限制;而線程池限制進(jìn)程中可以同時(shí)處于活動(dòng)狀態(tài)的線程數(shù)(默認(rèn)情況下,限制每個(gè) CPU 可以使用 25 個(gè)工作者線程和 1,000 個(gè) I/O 線程(根據(jù)機(jī)器CPU個(gè)數(shù)和.net framework版本的不同,這些數(shù)據(jù)可能會(huì)有變化)),所有大于此數(shù)目的請(qǐng)求將保持排隊(duì)狀態(tài),直到線程池線程變?yōu)榭捎谩?/span>

不建議更改線程池中的最大線程數(shù):

a)???????? 將線程池大小設(shè)置得太大,可能會(huì)造成更頻繁的執(zhí)行上下文切換及加劇資源的爭(zhēng)用情況。

b)???????? 其實(shí)FileStream的異步讀寫,異步發(fā)送接受Web請(qǐng)求,System.Threading.Timer定時(shí)器,甚至使用delegate的beginInvoke都會(huì)默認(rèn)調(diào)用 ThreadPool,也就是說不僅你的代碼可能使用到線程池,框架內(nèi)部也可能使用到。

c)???????? 一個(gè)應(yīng)用程序池是一個(gè)獨(dú)立的進(jìn)程,擁有一個(gè)線程池,應(yīng)用程序池中可以有多個(gè)WebApplication,每個(gè)運(yùn)行在一個(gè)單獨(dú)的AppDomain中,這些WebApplication公用一個(gè)線程池。

?

2)???????? 使用GetMinThreads()和SetMinThreads()獲取和設(shè)置最小空閑線程數(shù)

為避免向線程分配不必要的堆??臻g,線程池按照一定的時(shí)間間隔創(chuàng)建新的空閑線程(該間隔為半秒)。所以如果最小空閑線程數(shù)設(shè)置的過小,在短期內(nèi)執(zhí)行大量任務(wù)會(huì)因?yàn)閯?chuàng)建新空閑線程的內(nèi)置延遲導(dǎo)致性能瓶頸。最小空閑線程數(shù)默認(rèn)值等于機(jī)器上的CPU核數(shù),并且不建議更改最小空閑線程數(shù)

在啟動(dòng)線程池時(shí),線程池具有一個(gè)內(nèi)置延遲,用于啟用最小空閑線程數(shù),以提高應(yīng)用程序的吞吐量。

在線程池運(yùn)行中,對(duì)于執(zhí)行完任務(wù)的線程池線程,不會(huì)立即銷毀,而是返回到線程池,線程池會(huì)維護(hù)最小的空閑線程數(shù)(即使應(yīng)用程序所有線程都是空閑狀態(tài)),以便隊(duì)列任務(wù)可以立即啟動(dòng)。超過此最小數(shù)目的空閑線程一段時(shí)間沒事做后會(huì)自己醒來終止自己,以節(jié)省系統(tǒng)資源。

3)???????? 靜態(tài)方法GetAvailableThreads()

通過靜態(tài)方法GetAvailableThreads()返回的線程池線程的最大數(shù)目和當(dāng)前活動(dòng)數(shù)目之間的差值,即獲取線程池中當(dāng)前可用的線程數(shù)目

4)???????? 兩個(gè)參數(shù)

方法GetMaxThreads()、SetMaxThreads()、GetMinThreads()、SetMinThreads()、GetAvailableThreads()鈞包含兩個(gè)參數(shù)。參數(shù)workerThreads指工作者線程;參數(shù)completionPortThreads指異步 I/O 線程。

2.???????? 排隊(duì)工作項(xiàng)

通過調(diào)用 ThreadPool.QueueUserWorkItem 并傳遞 WaitCallback 委托來使用線程池。也可以通過使用 ThreadPool.RegisterWaitForSingleObject 并傳遞 WaitHandle(在向其發(fā)出信號(hào)或超時(shí)時(shí),它將引發(fā)對(duì)由 WaitOrTimerCallback 委托包裝的方法的調(diào)用)來將與等待操作相關(guān)的工作項(xiàng)排隊(duì)到線程池中。若要取消等待操作(即不再執(zhí)行WaitOrTimerCallback委托),可調(diào)用RegisterWaitForSingleObject()方法返回的RegisteredWaitHandle的 Unregister 方法。

如果您知道調(diào)用方的堆棧與在排隊(duì)任務(wù)執(zhí)行期間執(zhí)行的所有安全檢查不相關(guān),則還可以使用不安全的方法 ThreadPool.UnsafeQueueUserWorkItem 和 ThreadPool.UnsafeRegisterWaitForSingleObject。QueueUserWorkItem 和 RegisterWaitForSingleObject 都會(huì)捕獲調(diào)用方的堆棧,此堆棧將在線程池線程開始執(zhí)行任務(wù)時(shí)合并到線程池線程的堆棧中。如果需要進(jìn)行安全檢查,則必須檢查整個(gè)堆棧,但它還具有一定的性能開銷。使用“不安全的”方法調(diào)用并不會(huì)提供絕對(duì)的安全,但它會(huì)提供更好的性能。

3.?????????在一個(gè)內(nèi)核構(gòu)造可用時(shí)調(diào)用一個(gè)方法

讓一個(gè)線程不確定地等待一個(gè)內(nèi)核對(duì)象進(jìn)入可用狀態(tài),這對(duì)線程的內(nèi)存資源來說是一種浪費(fèi)。ThreadPool.RegisterWaitForSingleObject()為我們提供了一種方式:在一個(gè)內(nèi)核對(duì)象變得可用的時(shí)候調(diào)用一個(gè)方法。

使用需注意:

1)?????????WaitOrTimerCallback委托參數(shù),該委托接受一個(gè)名為timeOut的Boolean參數(shù)。如果?WaitHandle?在指定時(shí)間內(nèi)沒有收到信號(hào)(即,超時(shí)),則為?true,否則為?false。回調(diào)方法可以根據(jù)timeOut的值來針對(duì)性地采取措施。

2)?????????名為executeOnlyOnce的Boolean參數(shù)。傳true則表示線程池線程只執(zhí)行回調(diào)方法一次;若傳false則表示內(nèi)核對(duì)象每次收到信號(hào),線程池線程都會(huì)執(zhí)行回調(diào)方法。等待一個(gè)AutoResetEvent對(duì)象時(shí),這個(gè)功能尤其有用。

3)?????????RegisterWaitForSingleObject()方法返回一個(gè)RegisteredWaitHandle對(duì)象的引用。這個(gè)對(duì)象標(biāo)識(shí)了線程池正在它上面等待的內(nèi)核對(duì)象。我們可以調(diào)用它的Unregister(WaitHandle waitObject)方法取消由RegisterWaitForSingleObject()注冊(cè)的等待操作(即WaitOrTimerCallback委托不再執(zhí)行)。Unregister(WaitHandle waitObject)的WaitHandle參數(shù)表示成功取消注冊(cè)的等待操作后線程池會(huì)向此對(duì)象發(fā)出信號(hào)(set()),若不想收到此通知可以傳遞null。

?????????示例:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 private?static?void?Example_RegisterWaitForSingleObject() { ????// 加endWaitHandle的原因:如果執(zhí)行過快退出方法會(huì)導(dǎo)致一些東西被釋放,造成排隊(duì)的任務(wù)不能執(zhí)行,原因還在研究 ????AutoResetEvent endWaitHandle = new?AutoResetEvent(false); ? ????AutoResetEvent notificWaitHandle = new?AutoResetEvent(false); ????AutoResetEvent waitHandle = new?AutoResetEvent(false); ????RegisteredWaitHandle registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject( ????????waitHandle, ????????(Object state, bool?timedOut) => ????????{ ????????????if?(timedOut) ????????????????Console.WriteLine("RegisterWaitForSingleObject因超時(shí)而執(zhí)行"); ????????????else ????????????????Console.WriteLine("RegisterWaitForSingleObject收到WaitHandle信號(hào)"); ????????}, ????????null, TimeSpan.FromSeconds(2), true ?????); ? ????// 取消等待操作(即不再執(zhí)行WaitOrTimerCallback委托) ????registeredWaitHandle.Unregister(notificWaitHandle); ? ????// 通知 ????ThreadPool.RegisterWaitForSingleObject( ????????notificWaitHandle, ????????(Object state, bool?timedOut) => ????????{ ????????????if?(timedOut) ????????????????Console.WriteLine("第一個(gè)RegisterWaitForSingleObject沒有調(diào)用Unregister()"); ????????????else ????????????????Console.WriteLine("第一個(gè)RegisterWaitForSingleObject調(diào)用了Unregister()"); ? ????????????endWaitHandle.Set(); ????????}, ????????null, TimeSpan.FromSeconds(4), true ?????); ? ????endWaitHandle.WaitOne(); }

?

執(zhí)行上下文

???????? 上一小節(jié)中說到:線程池最大線程數(shù)設(shè)置過大可能會(huì)造成Windows頻繁執(zhí)行上下文切換,降低程序性能。對(duì)于大多數(shù)園友不會(huì)滿意這樣的回答,我和你一樣也喜歡“知其然,再知其所以然”。

1.???????? 上下文切換中的“上下文”是什么?

.NET中上下文太多,我最后得出的結(jié)論是:上下文切換中的上下文專指“執(zhí)行上下文”。

執(zhí)行上下文包括:安全上下文、同步上下文(System.Threading.SynchronizationContext)、邏輯調(diào)用上下文(System.Runtime.Messaging.CallContext)。即:安全設(shè)置(壓縮棧、Thread的Principal屬性和Windows身份)、宿主設(shè)置(System.Threading.HostExcecutingContextManager)以及邏輯調(diào)用上下文數(shù)據(jù)(System.Runtime.Messaging.CallContext的LogicalSetData()和LogicalGetData()方法)。

2.???????? 何時(shí)執(zhí)行“上下文切換”?

當(dāng)一個(gè)“時(shí)間片”結(jié)束時(shí),如果Windows決定再次調(diào)度同一個(gè)線程,那么Windows不會(huì)執(zhí)行上下文切換。如果Windows調(diào)度了一個(gè)不同的線程,這時(shí)Windows執(zhí)行線程上下文切換。

3.???????? “上下文切換”造成的性能影響

???????? 當(dāng)Windows上下文切換到另一個(gè)線程時(shí),CPU將執(zhí)行一個(gè)不同的線程,而之前線程的代碼和數(shù)據(jù)還在CPU的高速緩存中,(高速緩存使CPU不必經(jīng)常訪問RAM,RAM的速度比CPU高速緩存慢得多),當(dāng)Windows上下文切換到一個(gè)新線程時(shí),這個(gè)新線程極有可能要執(zhí)行不同的代碼并訪問不同的數(shù)據(jù),這些代碼和數(shù)據(jù)不在CPU的高速緩存中。因此,CPU必須訪問RAM來填充它的高速緩存,以恢復(fù)高速執(zhí)行狀態(tài)。但是,在其“時(shí)間片”執(zhí)行完后,一次新的線程上下文切換又發(fā)生了。

上下文切換所產(chǎn)生的開銷不會(huì)換來任何內(nèi)存和性能上的收益。執(zhí)行上下文所需的時(shí)間取決于CPU架構(gòu)和速度(即“時(shí)間片”的分配)。而填充CPU緩存所需的時(shí)間取決于系統(tǒng)運(yùn)行的應(yīng)用程序、CPU、緩存的大小以及其他各種因素。所以,無法為每一次線程上下文切換的時(shí)間開銷給出一個(gè)確定的值,甚至無法給出一個(gè)估計(jì)的值。唯一確定的是,如果要構(gòu)建高性能的應(yīng)用程序和組件,就應(yīng)該盡可能避免線程上下文切換。

除此之外,執(zhí)行垃圾回收時(shí),CLR必須掛起(暫停)所有線程,遍歷它們的棧來查找根以便對(duì)堆中的對(duì)象進(jìn)行標(biāo)記,再次遍歷它們的棧(有的對(duì)象在壓縮期間發(fā)生了移動(dòng),所以要更新它們的根),再恢復(fù)所有線程。所以,減少線程的數(shù)量也會(huì)顯著提升垃圾回收器的性能。每次使用一個(gè)調(diào)試器并遇到一個(gè)斷點(diǎn),Windows都會(huì)掛起正在調(diào)試的應(yīng)用程序中的所有線程,并在單步執(zhí)行或運(yùn)行應(yīng)用程序時(shí)恢復(fù)所有線程。因此,你用的線程越多,調(diào)試體驗(yàn)也就越差。

4.???????? 監(jiān)視Windows上下文切換工具

Windows實(shí)際記錄了每個(gè)線程被上下文切換到的次數(shù)??梢允褂孟馦icrosoft Spy++這樣的工具查看這個(gè)數(shù)據(jù)。這個(gè)工具是Visual Studio附帶的一個(gè)小工具(vs按安裝路徑\Visual Studio 2012\Common7\Tools),如圖

5.???????? 執(zhí)行上下文類詳解

在《異步編程:線程概述及使用》中我提到了Thread的兩個(gè)上下文,即:

1)???????? CurrentContext??????? 獲取線程正在其中執(zhí)行的當(dāng)前上下文。主要用于線程內(nèi)部存儲(chǔ)數(shù)據(jù)。

2)???????? ExecutionContext??? 獲取一個(gè)System.Threading.ExecutionContext對(duì)象,該對(duì)象包含有關(guān)當(dāng)前線程的各種上下文的信息。主要用于線程間數(shù)據(jù)共享。

其中獲取到的System.Threading.ExecutionContext就是本小節(jié)要說的“執(zhí)行上下文”。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public?sealed?class?ExecutionContext : IDisposable, ISerializable { ????public?void?Dispose(); ????public?void?GetObjectData(SerializationInfo info, StreamingContext context); ? ????// 此方法對(duì)于將執(zhí)行上下文從一個(gè)線程傳播到另一個(gè)線程非常有用。 ????public?ExecutionContext CreateCopy(); ????// 從當(dāng)前線程捕獲執(zhí)行上下文的一個(gè)副本。 ????public?static?ExecutionContext Capture(); ????// 在當(dāng)前線程上的指定執(zhí)行上下文中運(yùn)行某個(gè)方法。 ????public?static?void?Run(ExecutionContext executionContext, ContextCallback callback, object?state); ? ????// 取消執(zhí)行上下文在異步線程之間的流動(dòng)。 ????public?static?AsyncFlowControl SuppressFlow(); ????public?static?bool?IsFlowSuppressed(); ????// RestoreFlow? 撤消以前的 SuppressFlow 方法調(diào)用的影響。 ????// 此方法由 SuppressFlow 方法返回的 AsyncFlowControl 結(jié)構(gòu)的 Undo 方法調(diào)用。 ????// 應(yīng)使用 Undo 方法(而不是 RestoreFlow 方法)恢復(fù)執(zhí)行上下文的流動(dòng)。 ????public?static?void?RestoreFlow(); }

ExecutionContext 類提供的功能讓用戶代碼可以在用戶定義的異步點(diǎn)之間捕獲和傳輸此上下文。公共語言運(yùn)行時(shí)(CLR)確保在托管進(jìn)程內(nèi)運(yùn)行時(shí)定義的異步點(diǎn)之間一致地傳輸 ExecutionContext。

每當(dāng)一個(gè)線程(初始線程)使用另一個(gè)線程(輔助線程)執(zhí)行任務(wù)時(shí),CLR會(huì)將前者的執(zhí)行上下文流向(復(fù)制到)輔助線程(注意這個(gè)自動(dòng)流向是單方向的)。這就確保了輔助線程執(zhí)行的任何操作使用的是相同的安全設(shè)置和宿主設(shè)置。還確保了初始線程的邏輯調(diào)用上下文可以在輔助線程中使用。

但執(zhí)行上下文的復(fù)制會(huì)造成一定的性能影響。因?yàn)閳?zhí)行上下文中包含大量信息,而收集所有這些信息,再把它們復(fù)制到輔助線程,要耗費(fèi)不少時(shí)間。如果輔助線程又采用了更多地輔助線程,還必須創(chuàng)建和初始化更多的執(zhí)行上下文數(shù)據(jù)結(jié)構(gòu)。

所以,為了提升應(yīng)用程序性能,我們可以阻止執(zhí)行上下文的流動(dòng)。當(dāng)然這只有在輔助線程不需要或者不訪問上下文信息的時(shí)候才能進(jìn)行阻止。

下面給出一個(gè)示例為了演示:

1)???????? 在線程間共享邏輯調(diào)用上下文數(shù)據(jù)(CallContext)。

2)???????? 為了提升性能,阻止\恢復(fù)執(zhí)行上下文的流動(dòng)。

3)???????? 在當(dāng)前線程上的指定執(zhí)行上下文中運(yùn)行某個(gè)方法。

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private?static?void?Example_ExecutionContext() { ????CallContext.LogicalSetData("Name", "小紅"); ????Console.WriteLine("主線程中Name為:{0}", CallContext.LogicalGetData("Name")); ? ????// 1)?? 在線程間共享邏輯調(diào)用上下文數(shù)據(jù)(CallContext)。 ????Console.WriteLine("1)在線程間共享邏輯調(diào)用上下文數(shù)據(jù)(CallContext)。"); ????ThreadPool.QueueUserWorkItem((Object obj) ????????=> Console.WriteLine("ThreadPool線程中Name為:\"{0}\"", CallContext.LogicalGetData("Name"))); ????Thread.Sleep(500); ????Console.WriteLine(); ????// 2)?? 為了提升性能,取消\恢復(fù)執(zhí)行上下文的流動(dòng)。 ????ThreadPool.UnsafeQueueUserWorkItem((Object obj) ????????=> Console.WriteLine("ThreadPool線程使用Unsafe異步執(zhí)行方法來取消執(zhí)行上下文的流動(dòng)。Name為:\"{0}\"" ????????, CallContext.LogicalGetData("Name")), null); ????Console.WriteLine("2)為了提升性能,取消/恢復(fù)執(zhí)行上下文的流動(dòng)。"); ????AsyncFlowControl flowControl = ExecutionContext.SuppressFlow(); ????ThreadPool.QueueUserWorkItem((Object obj) ????????=> Console.WriteLine("(取消ExecutionContext流動(dòng))ThreadPool線程中Name為:\"{0}\"", CallContext.LogicalGetData("Name"))); ????Thread.Sleep(500); ????// 恢復(fù)不推薦使用ExecutionContext.RestoreFlow() ????flowControl.Undo(); ????ThreadPool.QueueUserWorkItem((Object obj) ????????=> Console.WriteLine("(恢復(fù)ExecutionContext流動(dòng))ThreadPool線程中Name為:\"{0}\"", CallContext.LogicalGetData("Name"))); ????Thread.Sleep(500); ????Console.WriteLine(); ????// 3)?? 在當(dāng)前線程上的指定執(zhí)行上下文中運(yùn)行某個(gè)方法。(通過獲取調(diào)用上下文數(shù)據(jù)驗(yàn)證) ????Console.WriteLine("3)在當(dāng)前線程上的指定執(zhí)行上下文中運(yùn)行某個(gè)方法。(通過獲取調(diào)用上下文數(shù)據(jù)驗(yàn)證)"); ????ExecutionContext curExecutionContext = ExecutionContext.Capture(); ????ExecutionContext.SuppressFlow(); ????ThreadPool.QueueUserWorkItem( ????????(Object obj) => ????????{ ????????????ExecutionContext innerExecutionContext = obj as?ExecutionContext; ????????????ExecutionContext.Run(innerExecutionContext, (Object state) ????????????????=> Console.WriteLine("ThreadPool線程中Name為:\"{0}\""<br>?????????????????????? , CallContext.LogicalGetData("Name")), null); ????????} ????????, curExecutionContext ?????); }

結(jié)果如圖:

???????? 注意:

1)???????? 示例中“在當(dāng)前線程上的指定執(zhí)行上下文中運(yùn)行某個(gè)方法”:代碼中必須使用ExecutionContext.Capture()獲取當(dāng)前執(zhí)行上下文的一個(gè)副本。

a)???????? 若直接使用Thread.CurrentThread.ExecutionContext則會(huì)報(bào)“無法應(yīng)用以下上下文: 跨 AppDomains 封送的上下文、不是通過捕獲操作獲取的上下文或已作為 Set 調(diào)用的參數(shù)的上下文?!卞e(cuò)誤。

b)???????? 若使用Thread.CurrentThread.ExecutionContext.CreateCopy()會(huì)報(bào)“只能復(fù)制新近捕獲(ExecutionContext.Capture())的上下文”。

2)???????? 取消執(zhí)行上下文流動(dòng)除了使用ExecutionContext.SuppressFlow()方式外。還可以通過使用ThreadPool的UnsafeQueueUserWorkItem 和 UnsafeRegisterWaitForSingleObject來執(zhí)行委托方法。原因是不安全的線程池操作不會(huì)傳輸壓縮堆棧。每當(dāng)壓縮堆棧流動(dòng)時(shí),托管的主體、同步、區(qū)域設(shè)置和用戶上下文也隨之流動(dòng)。

?

線程池線程中的異常

線程池線程中未處理的異常將終止進(jìn)程。以下為此規(guī)則的三種例外情況:
1. 由于調(diào)用了 Abort,線程池線程中將引發(fā)ThreadAbortException。
2. 由于正在卸載應(yīng)用程序域,線程池線程中將引發(fā)AppDomainUnloadedException。
3. 公共語言運(yùn)行庫(kù)或宿主進(jìn)程將終止線程。

何時(shí)不使用線程池線程

現(xiàn)在大家都已經(jīng)知道線程池為我們提供了方便的異步API及托管的線程管理。那么是不是任何時(shí)候都應(yīng)該使用線程池線程呢?當(dāng)然不是,我們還是需要“因地制宜”的,在以下幾種情況下,適合于創(chuàng)建并管理自己的線程而不是使用線程池線程:

1.???????? 需要前臺(tái)線程。(線程池線程“始終”是后臺(tái)線程)

2.???????? 需要使線程具有特定的優(yōu)先級(jí)。(線程池線程都是默認(rèn)優(yōu)先級(jí),“不建議”進(jìn)行修改)

3.???????? 任務(wù)會(huì)長(zhǎng)時(shí)間占用線程。由于線程池具有最大線程數(shù)限制,因此大量占用線程池線程可能會(huì)阻止任務(wù)啟動(dòng)。

4.???????? 需要將線程放入單線程單元(STA)。(所有ThreadPool線程“始終”是多線程單元(MTA)中)

5.???????? 需要具有與線程關(guān)聯(lián)的穩(wěn)定標(biāo)識(shí),或使某一線程專用于某一任務(wù)。

?

?

  本博文介紹線程池以及其基礎(chǔ)對(duì)象池,ThreadPool類的使用及注意事項(xiàng),如何排隊(duì)工作項(xiàng)到線程池,執(zhí)行上下文及線程上下文傳遞問題……?

線程池雖然為我們提供了異步操作的便利,但是它不支持對(duì)線程池中單個(gè)線程的復(fù)雜控制致使我們有些情況下會(huì)直接使用Thread。并且它對(duì)“等待”操作、“取消”操作、“延續(xù)”任務(wù)等操作比較繁瑣,可能迫使你從新造輪子。微軟也想到了,所以在.NET4.0的時(shí)候加入了“并行任務(wù)”并在.NET4.5中對(duì)其進(jìn)行改進(jìn),想了解“并行任務(wù)”的園友可以先看看《(譯)關(guān)于Async與Await的FAQ》。

本節(jié)到此結(jié)束,感謝大家的觀賞。贊的話還請(qǐng)多推薦啊 (*^_^*)

?

?

?

?

參考資料:《CLR via C#(第三版)》

總結(jié)

以上是生活随笔為你收集整理的【转】1.2异步编程:使用线程池管理线程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 大肉大捧一进一出好爽视频动漫 | 欧美综合色区 | 黄色aaa视频 | 久久国产精品影院 | 在线视频免费观看一区 | 男生女生插插插 | 高潮毛片又色又爽免费 | 冲田杏梨一区二区三区 | a级片中文字幕 | 中文字幕四区 | 国产精品电影一区二区三区 | 久久99精品久久久久婷婷 | 色97色 | 午夜精品99 | 欧美高清69hd| 午夜三区| 91高跟黑色丝袜呻吟在线观看 | 人妻天天爽夜夜爽一区二区三区 | 国产人妻大战黑人20p | 91在线播放视频 | 九九久视频| 可以看毛片的网站 | 激情影音| 国产人妻大战黑人20p | 久久久99久久 | 色欧美日韩 | 91久久久久久久久久 | 日本高清视频在线 | 美女一区二区三区视频 | 欧美自拍偷拍一区二区 | 二区三区在线 | 天天干天天操天天干 | 69xx国产| 亚洲午夜剧场 | 熟女精品一区二区三区 | 99热一区 | 欧美体内she精高潮 日韩一区免费 | 精品久久五月天 | 亚洲一区二区综合 | 日韩人妻无码精品久久免费 | 人妖性做爰aaaa | 日韩精品中文在线 | 欧美日韩高清免费 | 亚欧洲精品在线视频 | 国产91色| 色男人的天堂 | 免费成人小视频 | 久久午夜精品 | 人体毛片| 中国一级片黄色一级片黄 | 黄色三级三级三级 | 国产日日干 | 中文字幕亚洲乱码熟女1区2区 | 中出少妇 | 色综合区| 亚洲精品888 | 久久国产一区 | 91啪在线观看 | av免费播放 | 好吊妞这里只有精品 | 日本加勒比在线 | 一区二区91| 亚洲欧美成人一区 | 国家队动漫免费观看在线观看晨光 | 国产精品伦一区二区三级视频 | 欧美激情网址 | 日韩在线观看 | 国产一区二区综合 | av永久免费在线观看 | 一区二区三区偷拍 | 日韩a级大片 | 熟女少妇一区二区三区 | 国产婷婷久久 | 日本一区二区在线播放 | 在线观看日本视频 | 欧美天天视频 | 日韩在线视频免费播放 | 日本黄色三级视频 | 国产电影免费观看高清完整版视频 | 色网站在线看 | 国产成人在线电影 | 久久都是精品 | 久久欧 | 久久泄欲网| 国产香蕉在线视频 | 美女狂揉羞羞的视频 | 在线观看网页视频 | 亚洲一区精品视频在线观看 | 日本a在线观看 | 最近中文字幕在线观看 | jizzjizz美国 | 九九热视频免费 | 国产一区二区视频在线观看 | 久色视频在线播放 | 国产在线一卡二卡 | 国产愉拍 | 蜜臀久久精品久久久久久酒店 | 国内精品视频 | 天天色视频 |