【转】1.1异步编程:线程概述及使用
?
???????? 從此圖中我們會發(fā)現(xiàn) .NET 與C# 的每個版本發(fā)布都是有一個“主題”。即:C#1.0托管代碼→C#2.0泛型→C#3.0LINQ→C#4.0動態(tài)語言→C#5.0異步編程。現(xiàn)在我為最新版本的“異步編程”主題寫系列分享,期待你的查看及點(diǎn)評。
?
傳送門:異步編程系列目錄……
?
開始:《異步編程:線程概述及使用》
示例:異步編程:線程概述及使用.rar
???????? 做交互式客戶端應(yīng)用程序,用戶總希望程序能時刻響應(yīng)UI操作;做高性能服務(wù)器開發(fā),使用者總希望服務(wù)器能同時處理多個請求……等等,這時我們可以使用多線程技術(shù)來保證UI線程可響應(yīng)、提高服務(wù)器吞吐量、提升程序處理速度,設(shè)置任務(wù)優(yōu)先級進(jìn)行調(diào)度……
??? 多線程技術(shù)只是多個線程在操作系統(tǒng)分配的不同時間片里執(zhí)行,并不是程序開12個線程12個線程都在同一個 “時間點(diǎn)”執(zhí)行,同一“時間點(diǎn)”能執(zhí)行多少線程由CPU決定,各個執(zhí)行線程的銜接由操作系統(tǒng)進(jìn)行調(diào)度。即,在線程數(shù)量超出用于處理它們的處理器數(shù)量的情況下,操作系統(tǒng)將定期為每個線程調(diào)度一個時間片來控制處理器,以此來模擬同時并發(fā)。
??? 在認(rèn)識線程前,我們需要了解下CPU,了解下進(jìn)程。
?
多核心CPU超線程CPU
1.???????? 多核心處理器(CPU)
指在一塊處理器(CPU)中含有多個處理單元,每一個處理單元它就相當(dāng)于一個單核處理器(CPU)。因此,多核處理器的功能就相當(dāng)于多臺單核處理器電腦聯(lián)機(jī)作戰(zhàn)。
2.???????? 超線程處理器(CPU)
指在一塊CPU中,用虛擬的方法將一個物理核心模擬成多個核心(一般情況是一個單物理核心,模擬成二個核心,也即所謂的二線程。只有當(dāng)線程數(shù)比物理核心數(shù)多才能叫超線程。如四核四線程并不是超線程,而四核八線程才能叫超線程)。
3.???????? 優(yōu)缺點(diǎn):
1)???????? 多核心是真正的物理核心,一塊多核心的處理器(CPU),就相當(dāng)于多塊單核心的處理器(CPU)相互協(xié)作。因此,從理論上說,多核心比超線程具有更高運(yùn)算能力。雖然多核心比超線程的運(yùn)算速度快很多,但多核心也有一個明顯的缺點(diǎn),那就是多核心的使用效率比超線程處理器(CPU)低。因?yàn)?#xff0c;多核心在處理數(shù)據(jù)時,它們相互“合作”的并不是很完美,常常某個核心需要等待其他核心的計算數(shù)據(jù),從而耽誤時間,被迫怠工。另外,由于目前多核心都是采用共享緩存,這更使多核心的CPU運(yùn)算速度減慢不少(因?yàn)?#xff1a;CPU讀取Cache時是以行為單位讀取的,如果兩個硬件線程的兩塊不同內(nèi)存位于同一Cache行里,那么當(dāng)兩個硬件線程同時在對各自的內(nèi)存進(jìn)行寫操作時,將會造成兩個硬件線程寫同一Cache行的問題,它會引起競爭)。
2)???????? 超線程是用虛擬的方法將一個物理核心虛擬成多個核心,它能夠最大限度地利用現(xiàn)有的核心資源,具有較高性價比。
?
操作系統(tǒng)對多核處理器的支持
主要體現(xiàn)在調(diào)度和中斷上:
1.???????? 對任務(wù)的分配進(jìn)行優(yōu)化。使同一應(yīng)用程序的任務(wù)盡量在同一個核上執(zhí)行。
2.???????? 對任務(wù)的共享數(shù)據(jù)優(yōu)化。由于多核處理器(Chip Multi-Processor,CMP)體系結(jié)構(gòu)共享緩存(目前),可以考慮改變?nèi)蝿?wù)在內(nèi)存中的數(shù)據(jù)分布,使任務(wù)在執(zhí)行時盡量增加緩存的命中率。
3.???????? 對任務(wù)的負(fù)載均衡優(yōu)化。當(dāng)任務(wù)在調(diào)度時,出現(xiàn)了負(fù)載不均衡,考慮將較忙處理器中與其他任務(wù)最不相關(guān)的任務(wù)遷移,以達(dá)到數(shù)據(jù)的沖突最小。
4.?????? 支持搶先多任務(wù)處理的操作系統(tǒng)可以創(chuàng)建多個進(jìn)程中的多個線程同時執(zhí)行的效果。它通過以下方式實(shí)現(xiàn)這一點(diǎn):在需要處理器時間的線程之間分割可用處理器時間,并輪流為每個線程分配處理器時間片。當(dāng)前執(zhí)行的線程在其時間片結(jié)束時被掛起,而另一個線程繼續(xù)運(yùn)行。當(dāng)系統(tǒng)從一個線程切換到另一個線程時,它將保存被搶先的線程的線程上下文,并重新加載線程隊列中下一個線程的已保存線程上下文。
?
進(jìn)程和線程
1.???????? 進(jìn)程
進(jìn)程是應(yīng)用程序的執(zhí)行實(shí)例,每個進(jìn)程是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它各種系統(tǒng)資源組成,進(jìn)程在運(yùn)行過程中創(chuàng)建的資源隨著進(jìn)程的終止而被銷毀,所使用的系統(tǒng)資源在進(jìn)程終止時被釋放或關(guān)閉。
2.???????? 線程
線程是進(jìn)程內(nèi)部的一個執(zhí)行單元。系統(tǒng)創(chuàng)建好進(jìn)程后,實(shí)際上就啟動執(zhí)行了該進(jìn)程的主執(zhí)行線程。主執(zhí)行線程終止了,進(jìn)程也就隨之終止。
每個線程都維護(hù)異常處理程序、調(diào)度優(yōu)先級和線程上下文。(線程上下文,當(dāng)前執(zhí)行的線程在其時間片結(jié)束時被掛起,而另一個線程繼續(xù)運(yùn)行。當(dāng)系統(tǒng)從一個線程切換到另一個線程時,它將保存被搶先的線程的線程上下文,并重新加載線程隊列中下一個線程的已保存線程上下文)
3.???????? 關(guān)系
操作系統(tǒng)使用進(jìn)程將它們正在執(zhí)行的不同應(yīng)用程序分開,.NET Framework 將操作系統(tǒng)進(jìn)程進(jìn)一步細(xì)分為System.AppDomain (應(yīng)用程序域)的輕量托管子進(jìn)程。
線程是CPU的調(diào)度單元,是進(jìn)程中的執(zhí)行單位,一個進(jìn)程中可以有多個線程同時執(zhí)行代碼。
?
操作系統(tǒng)中,CPU的兩種競爭策略
操作系統(tǒng)中,CPU競爭有很多種策略。Unix系統(tǒng)使用的是時間片算法,而Windows則屬于搶占式的。
1.???????在時間片算法中,所有的進(jìn)程排成一個隊列。操作系統(tǒng)按照他們的順序,給每個進(jìn)程分配一段時間,即該進(jìn)程允許運(yùn)行的時間。如果在時間片結(jié)束時進(jìn)程還在運(yùn)行,則CPU將被剝奪并分配給另一個進(jìn)程。如果進(jìn)程在時間片結(jié)束前阻塞或結(jié)束,則CPU當(dāng)即進(jìn)行切換。調(diào)度程序所要做的就是維護(hù)一張就緒進(jìn)程列表,當(dāng)進(jìn)程用完它的時間片后,它被移到隊列的末尾。
2.???????所謂搶占式操作系統(tǒng),就是說如果一個進(jìn)程得到了?CPU?時間,除非它自己放棄使用?CPU?,否則將完全霸占?CPU?。因此可以看出,在搶占式操作系統(tǒng)中,操作系統(tǒng)假設(shè)所有的進(jìn)程都是“人品很好”的,會主動退出?CPU?。在搶占式操作系統(tǒng)中,假設(shè)有若干進(jìn)程,操作系統(tǒng)會根據(jù)他們的優(yōu)先級、饑餓時間(已經(jīng)多長時間沒有使用過?CPU?了),給他們算出一個總的優(yōu)先級來。操作系統(tǒng)就會把?CPU?交給總優(yōu)先級最高的這個進(jìn)程。當(dāng)進(jìn)程執(zhí)行完畢或者自己主動掛起后,操作系統(tǒng)就會重新計算一次所有進(jìn)程的總優(yōu)先級,然后再挑一個優(yōu)先級最高的把?CPU?控制權(quán)交給他。
?
線程Thread類詳解
| 靜態(tài)屬性 | CurrentThread ,CurrentContext,CurrentPrincipal(負(fù)責(zé)人) | 
| 靜態(tài)方法 | AllocateDataSlot(),AllocateNamedDataSlot(),FreeNamedDataSlot(),GetNamedDataSlot(),GetData(),SetData(),BeginCriticalRegion()[關(guān)鍵的],EndCriticalRegion(),BeginThreadAffinity(),EndThreadAffinity(), GetDomain(),GetDomainID(), ResetAbort(),Sleep(),SpinWait(),MemoryBarrier(),VolatileRead(),VolatileWrite(),Yield() | 
| ? | |
| 實(shí)例屬性 | Priority,ThreadState ,IsAlive,IsBackground,IsThreadPoolThread,ManagedThreadId,ApartmentState,CurrentCulture,CurrentUICulture,ExecutionContext,Name | 
| 實(shí)例方法 | GetHashCode(),Start(),Abort(), Resume(),Suspend(),Join(),Interrupt(),GetApartmentState(),SetApartmentState(),TrySetApartmentState(),GetCompressedStack(),SetCompressedStack(),DisableComObjectEagerCleanup() | 
?
1.?????????常用屬性
1)???????? CurrentContext???????????????? 獲取線程正在其中執(zhí)行的當(dāng)前上下文。主要用于線程內(nèi)部存儲數(shù)據(jù)。
2)???????? ExecutionContext???????????? 獲取一個System.Threading.ExecutionContext對象,該對象包含有關(guān)當(dāng)前線程的各種上下文的信息。主要用于線程間數(shù)據(jù)共享。
?
3)???????? IsThreadPoolThread???????? 獲取一個值,該值指示線程是否屬于托管線程池。
4)???????? ManagedThreadId??????????? 獲取一個整數(shù),表示此托管線程的唯一標(biāo)識符。
5)???????? IsBackground???????????????????? 獲取或設(shè)置一個值,該值指示某個線程是否為后臺線程。
前臺線程和后臺線程并不等同于主線程和工作線程,如果所有的前臺線程終止,那所有的后臺線程也會被自動終止。應(yīng)用程序必須運(yùn)行完所有的前臺線程才可以退出,所以,要特別注意前臺線程的使用,會造成應(yīng)用程序終止不了。
默認(rèn)情況下:通過Thread.Start()方法開啟的線程都默認(rèn)為前臺線程。可以設(shè)置IsBackground屬性將線程配置為后臺線程。
屬于托管線程池的線程(即其 IsThreadPoolThread 屬性為 true 的線程)是后臺線程。從非托管代碼進(jìn)入托管執(zhí)行環(huán)境的所有線程都被標(biāo)記為后臺線程。
6)???????? IsAlive??????????????????????????? 判斷此線程是否還存活。經(jīng)測試只有 Unstarted、Stopped 返回false;其他線程狀態(tài)都返回true。
2.???????? 創(chuàng)建線程
| 1 2 3 4 | public?Thread(ParameterizedThreadStart start); public?Thread(ThreadStart start); public?Thread(ParameterizedThreadStart start, int?maxStackSize); public?Thread(ThreadStart start, int?maxStackSize); | 
Thread包含使用ThreadStart或ParameterizedThreadStart委托做參數(shù)的構(gòu)造函數(shù),這些委托包裝調(diào)用Start()時由新線程執(zhí)行的方法。
?????????線程一旦啟動,就不必保留對Thread對象的引用。線程會繼續(xù)執(zhí)行直到線程所調(diào)用委托執(zhí)行完畢。
1)???????? 向線程傳遞數(shù)據(jù)(見示例)
我們可以直接使用接收ParameterizedThreadStart參數(shù)Thread構(gòu)造函數(shù)創(chuàng)建新線程,再通過Start(object parameter)傳入?yún)?shù)并啟動線程。由于Start方法接收任何對象,所以這并不是一種類型安全的實(shí)現(xiàn)。
所以我們可以使用一種替代方案:將線程執(zhí)行的方法和待傳遞數(shù)據(jù)封裝在幫助器類中,使用無參的Start()啟動線程。必要的時候需在幫助器類中使用同步基元對象避免線程共享數(shù)據(jù)的死鎖和資源爭用。
2)?????????使用回調(diào)方法檢索數(shù)據(jù)(見示例)
Thread構(gòu)造函數(shù)接收的ThreadStart或ParameterizedThreadStart委托參數(shù),這兩個委托的聲明都是返回void,即線程執(zhí)行完后不會有數(shù)據(jù)返回(實(shí)際上主線程也不會等待Thread創(chuàng)建的新線程返回,否則創(chuàng)建新線程就無意義了)。那么如何在異步執(zhí)行完時做出響應(yīng)呢?使用回調(diào)方法。
??
示例----關(guān)鍵代碼(詳見Simple4CallBackWithParam()):
| 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 | // 包裝異步方法的委托 public?delegate?void?ExampleCallback(int?lineCount); // 幫助器類 ? public?class?ThreadWithState { ????private?string?boilerplate; ????private?int?value; ????private?ExampleCallback callback; ?? ????public?ThreadWithState(string?text, int?number, ????????ExampleCallback callbackDelegate) ? ????{ ????????boilerplate = text; ????????value = number; ????????callback = callbackDelegate; ????} ? ????public?void?ThreadProc() ????{ ????????Console.WriteLine(boilerplate, value); ????????// 異步執(zhí)行完時調(diào)用回調(diào) ????????if?(callback != null) ????????????callback(1); ????} } ?? ????// 異步調(diào)用 ????// 將需傳遞給異步執(zhí)行方法數(shù)據(jù)及委托傳遞給幫助器類 ????ThreadWithState tws = new?ThreadWithState( ???????"This report displays the number {0}.", ???????42, ???????new?ExampleCallback(ResultCallback) ????); ????Thread t = new?Thread(new?ThreadStart(tws.ThreadProc)); ????t.Start(); | 
?
3.???????? 調(diào)度線程
???????? 使用Thread.Priority屬性獲取或設(shè)置任何線程的優(yōu)先級。優(yōu)先級:Lowest <BelowNormal< Normal <AboveNormal< Highest
| 1 2 3 4 5 6 7 8 9 | public?enum?ThreadPriority { ????Lowest = 0, ????BelowNormal = 1, ????// 默認(rèn)情況下,線程具有 Normal 優(yōu)先級。 ????Normal = 2, ????AboveNormal = 3, ????Highest = 4, } | 
每個線程都具有分配給它的線程優(yōu)先級。在公共語言運(yùn)行庫中創(chuàng)建的線程最初分配的優(yōu)先級為ThreadPriority.Normal。在運(yùn)行庫外創(chuàng)建的線程會保留它們在進(jìn)入托管環(huán)境之前所具有的優(yōu)先級。
線程是根據(jù)其優(yōu)先級而調(diào)度執(zhí)行的。所有線程都是由操作系統(tǒng)分配處理器時間片的,如果具有相同優(yōu)先級的多個線程都可用,則計劃程序?qū)⒈闅v處于該優(yōu)先級的線程,并為每個線程提供一個“固定的時間片”來執(zhí)行,執(zhí)行完“固定的時間片”后就切換線程,若當(dāng)前任務(wù)還未執(zhí)行完,則必須等待下一次的調(diào)度。
低優(yōu)先級的線程并不是被阻塞直到較高優(yōu)先級的線程完成,低優(yōu)先級的線程只是在相同時間間隔被CPU調(diào)度的次數(shù)相對較少。
重要提示:
???????? 最好是降低一個線程的優(yōu)先級,而不是提升另一個線程的優(yōu)先級。如果線程要執(zhí)行一個長時間運(yùn)行的計算限制任務(wù),比如編譯代碼、拼寫檢查、電子表格重新計算等,一般應(yīng)降低該線程的優(yōu)先級。如果線程要快速響應(yīng)某個事件,然后運(yùn)行非常短暫的時間,再恢復(fù)為等待狀態(tài),則應(yīng)提高該線程的優(yōu)先級。高優(yōu)先級線程在其生命中的大多數(shù)時間里都應(yīng)處于等待狀態(tài),這樣才不至于影響系統(tǒng)的總體響應(yīng)能力。
?
4.???????? 線程狀態(tài)
Thread.ThreadState屬性提供一個位掩碼,用它指示線程的當(dāng)前狀態(tà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 | [Flags] public?enum?ThreadState { ????//線程已啟動,它未被阻塞,并且沒有掛起的 ThreadAbortException。 ????Running = 0, ????// 正在請求線程停止。 這僅用于內(nèi)部。 ????StopRequested = 1, ????// 正在請求線程掛起。 ????SuspendRequested = 2, ????// 線程正作為后臺線程執(zhí)行(相對于前臺線程而言)。 此狀態(tài)可以通過設(shè)置 Thread.IsBackground 屬性來控制。 ????Background = 4, ????// 尚未對線程調(diào)用 Thread.Start() 方法。 ????Unstarted = 8, ????// 線程已停止。 ????Stopped = 16, ????// 線程已被阻止。 這可能是因?yàn)?#xff1a;調(diào)用 Thread.Sleep(System.Int32) 或 Thread.Join()、請求鎖定(例如通過調(diào)用Monitor.Enter(System.Object) 或 Monitor.Wait(System.Object,System.Int32,System.Boolean))或等待線程同步對象(例如Threading.ManualResetEvent)。 ????WaitSleepJoin = 32, ????// 線程已掛起。 ????Suspended = 64, ????// 已對線程調(diào)用了 Thread.Abort(System.Object) 方法,但線程尚未收到試圖終止它的掛起的ThreadAbortException。 ????AbortRequested = 128, ????// 線程狀態(tài)包括 ThreadState.AbortRequested 并且該線程現(xiàn)在已死,但其狀態(tài)尚未更改為 ThreadState.Stopped。 ????Aborted = 256, } | 
由于 Running 狀態(tài)的值為 0 (枚舉的默認(rèn)值),因此不可能執(zhí)行位測試來發(fā)現(xiàn)此狀態(tài)。但可以使用此測試(以偽代碼表示):if ((state & (Unstarted | Stopped)) == 0){}
線程可以同時處于多個狀態(tài)中。例如,如果某個線程在 Monitor.Wait 調(diào)用被阻止,并且另一個線程對同一個線程調(diào)用 Abort,則該線程將同時處于 WaitSleepJoin 和 AbortRequested 狀態(tài)。在這種情況下,一旦該線程從對 Wait 的調(diào)用返回或該線程中斷,它就會收到 ThreadAbortException。
?
5.???????? 線程狀態(tài)操作方法
操作:Start(),Abort(),Suspend(),Resume(), Join(),Interrupt()以及靜態(tài)方法Sleep()和ResetAbort()
?
???????? 線程操作與線程狀態(tài)對應(yīng)的表和圖如下:
| 操作 | 所得到的新狀態(tài) | 
| 調(diào)用 Thread 類的構(gòu)造函數(shù)。 | Unstarted | 
| 另一個線程調(diào)用 Thread.Start。 | Unstarted | 
| 線程響應(yīng) Thread.Start 并開始運(yùn)行。 | Running | 
| 線程調(diào)用 Thread.Sleep。 | ? WaitSleepJoin | 
| 線程對另一個對象調(diào)用 Monitor.Wait。 | |
| 線程對另一個線程調(diào)用 Thread.Join。 | |
| 另一個線程調(diào)用 Thread.Suspend。 | SuspendRequested | 
| 線程返回到托管代碼時,線程響應(yīng) Thread.Suspend 請求。 | Suspended | 
| 另一個線程調(diào)用 Thread.Resume。 | Running | 
| 另一個線程調(diào)用 Thread.Abort。 | AbortRequested | 
| 線程返回到托管代碼時,線程響應(yīng) Thread.Abort。 | Aborted ,然后 Stopped | 
??
1)???????? 開始線程
調(diào)用Start()開始一個線程。一旦線程由于調(diào)用 Start 而離開 Unstarted 狀態(tài),那么它將無法再返回到 Unstarted 狀態(tài)(最后被銷毀)。
2)???????? 線程銷毀及取消銷毀
調(diào)用線程的Abort()實(shí)例方法可以銷毀目標(biāo)線程實(shí)例,調(diào)用Thread.ResetAbort() 來取消線程銷毀。()
請注意:
a)???????? 異常是在目標(biāo)線程捕獲,而不是主線程的try-catch-finally。
b)???????? 是“可以”銷毀目標(biāo)線程實(shí)例,不能保證線程會結(jié)束。因?yàn)?/span>
l? 目標(biāo)線程可捕捉 ThreadAbortException 異常并在此catch塊中調(diào)用Thread.ResetAbort() 來取消線程銷毀,取消后try塊外面的代碼可正常運(yùn)行。
l? 在finally塊中可以執(zhí)行任意數(shù)量的代碼(在finally中調(diào)用Thread.ResetAbort()不能取消線程的銷毀),若不給予超時設(shè)置也無法保證線程會結(jié)束。
c)???????? 注意Abort()后要在catch或finally中清理對象。
d)???????? 如果您希望一直等到被終止的線程結(jié)束,可以調(diào)用Thread.Join()方法。Join 是一個模塊化調(diào)用,它直到線程實(shí)際停止執(zhí)行時才返回。
e)???????? 如果調(diào)用線程的 Abort 方法時線程正在執(zhí)行非托管代碼,則運(yùn)行庫將其標(biāo)記為ThreadState.AbortRequested。待線程返回到托管代碼時引發(fā)ThreadAbortException異常。
f)????????? 一旦線程被中止ThreadState.Stoped,它將無法重新啟動。
?
示例----關(guān)鍵代碼(詳見Simple4Abort())
| 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 | Thread t = new?Thread( ??????() => ??????{ ??????????try ??????????{ ??????????????Console.WriteLine("try內(nèi)部,調(diào)用Abort前。"); ??????????????// ……等待其他線程調(diào)用該線程的Abort() ??????????????Console.WriteLine("try內(nèi)部,調(diào)用Abort后。"); ??????????} ??????????catch?(ThreadAbortException abortEx) ??????????{ ??????????????Console.WriteLine("catch:"?+ abortEx.GetType()); ??????????????Thread.ResetAbort(); ??????????????Console.WriteLine("catch:調(diào)用ResetAbort()。"); ??????????} ??????????catch?(Exception ex) ??????????{ ??????????????Console.WriteLine("catch:"?+ ex.GetType()); ??????????} ??????????finally ??????????{ ??????????????Console.WriteLine("finally"); ??????????????// 在finally中調(diào)用Thread.ResetAbort()不能取消線程的銷毀 ??????????????//Thread.ResetAbort(); ??????????????//Console.WriteLine("調(diào)用ResetAbort()。"); ??????????} ? ??????????Console.WriteLine("try外面,調(diào)用Abort后(若再catch中調(diào)用了ResetAbort,則try塊外面的代碼依舊執(zhí)行,即:線程沒有終止)。"); ??????} ? ?// 其他線程調(diào)用該線程的Abort() ?t.Abort(); ?Console.WriteLine("主線程,調(diào)用Abort。"); | 
輸出:
???????? 若在catch中沒有調(diào)用Thread.ResetAbort(),哪么try塊外面的代碼就不會輸出(詳見輸出截圖的兩處紅線)。
3)???????? 阻塞線程
調(diào)用Sleep()方法使當(dāng)前線程放棄剩余時間片,立即掛起(阻塞)并且在指定時間內(nèi)不被調(diào)度。
Sleep(timeout),會有條件地將調(diào)用線程從當(dāng)前處理器上移除,并且有可能將它從線程調(diào)度器的可運(yùn)行隊列中移除。這個條件取決于調(diào)用 Sleep 時timeout 參數(shù)。
a)?? 當(dāng) timeout = 0, 即 Sleep(0),如果線程調(diào)度器的可運(yùn)行隊列中有大于或等于當(dāng)前線程優(yōu)先級的就緒線程存在,操作系統(tǒng)會將當(dāng)前線程從處理器上移除,調(diào)度其他優(yōu)先級高的就緒線程運(yùn)行;如果可運(yùn)行隊列中的沒有就緒線程或所有就緒線程的優(yōu)先級均低于當(dāng)前線程優(yōu)先級,那么當(dāng)前線程會繼續(xù)執(zhí)行,就像沒有調(diào)用 Sleep(0)一樣。一個時間片結(jié)束時,如果Windows決定再次調(diào)度同一個線程(而不是切換到另一個線程),那么Windows不會執(zhí)行上下文切換。
b)?? 當(dāng) timeout > 0 時,如:Sleep(1),可能會引發(fā)線程上下文切換(如果發(fā)生線程切換):調(diào)用線程會從線程調(diào)度器的可運(yùn)行隊列中被移除一段時間,這個時間段約等于 timeout 所指定的時間長度。為什么說約等于呢?是因?yàn)樗邥r間單位為毫秒,這與系統(tǒng)的時間精度有關(guān)。通常情況下,系統(tǒng)的時間精度為 10 ms,那么指定任意少于 10 ms但大于 0 ms 的睡眠時間,均會向上求值為 10 ms。
調(diào)用Thread.Sleep(Timeout.Infinite)將使線程休眠,直到其他運(yùn)行線程調(diào)用 Interrupt ()中斷處于WaitSleepJoin線程狀態(tài)的線程,或調(diào)用Abort()中止線程。
?
應(yīng)用實(shí)例:輪詢休眠
while (!proceed) Thread.Sleep (x);??? // "輪詢休眠!"
4)???????? 線程的掛起和喚醒
可結(jié)合Suspend()與Resume()來掛起和喚醒線程,這兩方法已過時。
當(dāng)對某線程調(diào)用Suspend()時,系統(tǒng)會讓該線程執(zhí)行到一個安全點(diǎn),然后才實(shí)際掛起該線程(與Thread.Sleep()不同, Suspend()不會導(dǎo)致線程立即停止執(zhí)行)。無論調(diào)用了多少次 Suspend(),調(diào)用Resume()均會使另一個線程脫離掛起狀態(tài),并導(dǎo)致該線程繼續(xù)執(zhí)行。
注意:由于Suspend()和Resume()不依賴于受控制線程的協(xié)作,因此,它們極具侵犯性并且會導(dǎo)致嚴(yán)重的應(yīng)用程序問題,如死鎖(例如,如果您在安全權(quán)限評估期間掛起持有鎖的線程,則AppDomain中的其他線程可能被阻止。如果您在線程正在執(zhí)行類構(gòu)造函數(shù)時掛起它,則AppDomain中試圖使用該類的其他線程將被阻止。很容易發(fā)生死鎖)。
線程的安全點(diǎn):
是線程執(zhí)行過程中可執(zhí)行垃圾回收的一個點(diǎn)。垃圾回收器在執(zhí)行垃圾回收時,運(yùn)行庫必須掛起除正在執(zhí)行回收的線程以外的所有線程。每個線程在可以掛起之前都必須置于安全點(diǎn)。
5)???????? Join()
在線程A中調(diào)用線程B的Join()實(shí)例方法。在繼續(xù)執(zhí)行標(biāo)準(zhǔn)的 COM 和 SendMessage 消息泵處理期間,線程A將被阻塞,直到線程B終止為止。
6)???????? Interrupt()
中斷處于WaitSleepJoin線程狀態(tài)的線程。如果此線程當(dāng)前未阻塞在等待、休眠或聯(lián)接狀態(tài)中,則下次開始阻塞時它將被中斷并引發(fā)ThreadInterruptedException異常。
線程應(yīng)該捕獲ThreadInterruptedException并執(zhí)行任何適當(dāng)?shù)牟僮饕岳^續(xù)運(yùn)行。如果線程忽略該異常,則運(yùn)行庫將捕獲該異常并停止該線程。
如果調(diào)用線程的 Interrupt()方法時線程正在執(zhí)行非托管代碼,則運(yùn)行庫將其標(biāo)記為ThreadState.SuspendRequested。待線程返回到托管代碼時引發(fā)ThreadInterruptedException異常。
6.???????? SpinWait(int iterations)
SpinWait實(shí)質(zhì)上會將處理器置于十分緊密的自旋轉(zhuǎn)中,當(dāng)前線程一直占用CPU,其循環(huán)計數(shù)由 iterations 參數(shù)指定。
SpinWait并不是一個阻止的方法:一個處于spin-waiting的線程的ThreadState不是WaitSleepJoin狀態(tài),并且也不會被其它的線程過早的中斷(Interrupt)。SpinWait的作用是等待一個在極短時間(可能小于一微秒)內(nèi)可準(zhǔn)備好的可預(yù)期的資源,而避免調(diào)用Sleep()方法阻止線程而浪費(fèi)CPU時間(上下文切換)。
優(yōu)點(diǎn):避免線程上下文切換的耗時操作。
缺點(diǎn):CPU不能很好的調(diào)度CPU利用率。這種技術(shù)的優(yōu)勢只能在多處理器計算機(jī)上體現(xiàn),對單一處理器的電腦,直到輪詢的線程結(jié)束了它的時間片之前,別的資源無法獲得cpu調(diào)度執(zhí)行。
7.???????? 設(shè)置和獲取線程的單元狀態(tài)
| 1 2 3 4 5 6 7 8 9 10 | // System.Threading.Thread 的單元狀態(tài)。 public?enum?ApartmentState { ????// System.Threading.Thread 將創(chuàng)建并進(jìn)入一個單線程單元。 ????STA = 0, ????// System.Threading.Thread 將創(chuàng)建并進(jìn)入一個多線程單元。 ????MTA = 1, ????// 尚未設(shè)置 System.Threading.Thread.ApartmentState 屬性。 ????Unknown = 2, } | 
1)???????? 可使用ApartmentState獲取和設(shè)置線程的單元狀態(tài),次屬性已經(jīng)過時
2)???????? SetApartmentState()+TrySetApartmentState()+GetApartentState()
可以標(biāo)記一個托管線程以指示它將承載一個單線程或多線程單元。如果未設(shè)置該狀態(tài),則GetApartmentState返回ApartmentState.Unknown。只有當(dāng)線程處于ThreadState.Unstarted狀態(tài)時(即線程還未調(diào)用Start()時)才可以設(shè)置該屬性;一個線程只能設(shè)置一次。
如果在啟動線程之前未設(shè)置單元狀態(tài),則該線程被初始化為默認(rèn)多線程單元 (MTA)。(終結(jié)器線程和由ThreadPool控制的所有線程都是 MTA)
要將主應(yīng)用程序線程的單元狀態(tài)設(shè)置為ApartmentState.STA的唯一方法是將STAThreadAttribute屬性應(yīng)用到入口點(diǎn)方法。(eg:Main()方法)
8.?????????設(shè)置和檢索線程數(shù)據(jù)(數(shù)據(jù)槽)
線程使用托管線程本地存儲區(qū) (TLS,Thread-Local Storage)來存儲線程特定的數(shù)據(jù),托管 TLS 中的數(shù)據(jù)都是線程和應(yīng)用程序域組合所獨(dú)有的,其他任何線程(即使是子線程)都無法獲取這些數(shù)據(jù)。
公共語言運(yùn)行庫在創(chuàng)建每個進(jìn)程時給它分配一個多槽數(shù)據(jù)存儲區(qū)數(shù)組,數(shù)據(jù)槽包括兩種類型:命名槽和未命名槽。
1)???????? 若要創(chuàng)建命名數(shù)據(jù)槽,使用 Thread.AllocateNamedDataSlot() 或 Thread.GetNamedDataSlot() 方法。命名數(shù)據(jù)槽數(shù)據(jù)必須使用Thread.FreeNamedDataSlot()來釋放。
在任何線程調(diào)用Thread.FreeNamedDataSlot()之后,后面任何線程使用相同名稱調(diào)用Thread.GetNamedDataSlot()都將返回新槽。但是,任何仍具有以前通過調(diào)用Thread.GetNamedDataSlot()返回的System.LocalDataStoreSlot引用的線程可以繼續(xù)使用舊槽。
只有當(dāng)調(diào)用Thread.FreeNamedDataSlot()之前獲取的所有LocalDataStoreSlot已被釋放并進(jìn)行垃圾回收之后,與名稱關(guān)聯(lián)的槽才會被釋放。
2)???????? 若要獲取對某個現(xiàn)有命名槽的引用,將其名稱傳遞給 Thread.GetNamedDataSlot() 方法。
3)???????? 若要創(chuàng)建未命名數(shù)據(jù)槽,使用 Thread.AllocateDataSlot() 方法。未命名數(shù)據(jù)槽數(shù)據(jù)在線程終止后釋放。
4)???????? 對于命名槽和未命名槽,使用 Thread.SetData() 和 Thread.GetData() 方法設(shè)置和檢索槽中的信息。
命名槽可能很方便,因?yàn)槟梢栽谛枰鼤r通過將其名稱傳遞給 GetNamedDataSlot 方法來檢索該槽,而不是維護(hù)對未命名槽的引用。但是,如果另一個組件使用相同的名稱來命名其線程相關(guān)的存儲區(qū),并且有一個線程同時執(zhí)行來自您的組件和該組件的代碼,則這兩個組件可能會破壞彼此的數(shù)據(jù)。(本方案假定這兩個組件在同一應(yīng)用程序域內(nèi)運(yùn)行,并且它們并不用于共享相同數(shù)據(jù)。)
為了獲得更好的性能,請改用以 System.ThreadStaticAttribute特性標(biāo)記的線程相關(guān)的靜態(tài)字段。
9.?????????原子操作
由于編譯器,或者CPU的優(yōu)化,可能導(dǎo)致程序執(zhí)行的時候并不是真正的按照代碼順序執(zhí)行。在多線程開發(fā)的時候可能會引起錯誤。
在debug模式下,編譯器不會做任何優(yōu)化,而當(dāng)Release后,編譯器做了優(yōu)化,此時就會出現(xiàn)問題。
1)???????? Thread.MemoryBarrier()
按如下方式同步內(nèi)存存取:執(zhí)行當(dāng)前線程的處理器在對指令重新排序時,不能采用先執(zhí)行 Thread.MemoryBarrier()調(diào)用之后的內(nèi)存存取,再執(zhí)行 Thread.MemoryBarrier() 調(diào)用之前的內(nèi)存存取的方式。
2)???????? Thread.VolatileRead()+Thread.VolatileWrite()???(內(nèi)部使用MemoryBarrier()內(nèi)存屏障)
a)???????? VolatileRead()?????????? 讀取字段值。無論處理器的數(shù)目或處理器緩存的狀態(tài)如何,該值都是由計算機(jī)的任何處理器寫入的最新值。
b)???????? VolatileWrite ()???????? 立即向字段寫入一個值,以使該值對計算機(jī)中的所有處理器都可見。
3)???????? 關(guān)鍵字Volatile:
為了簡化編程,C#編譯器提供了volatile關(guān)鍵字。確保JIT編譯器對易失字段都以易失讀取或者易失寫入的方法執(zhí)行,不用顯示調(diào)用Thread的VolatileRead()和VolatileWrite()。
10.???? BeginCriticalRegion()+EndCriticalRegion()?? (Critical:關(guān)鍵性的)
若要通知宿主代碼進(jìn)入關(guān)鍵區(qū)域,調(diào)用BeginCriticalRegion。當(dāng)執(zhí)行返回到非關(guān)鍵代碼區(qū)域時,調(diào)用EndCriticalRegion。
公共語言運(yùn)行庫 (CLR) 的宿主可在關(guān)鍵代碼區(qū)域和非關(guān)鍵代碼區(qū)域建立不同的失敗策略。關(guān)鍵區(qū)域是指線程中止或未處理異常的影響可能不限于當(dāng)前任務(wù)的區(qū)域。相反,非關(guān)鍵代碼區(qū)域中的中止或失敗只對出現(xiàn)錯誤的任務(wù)有影響。
當(dāng)關(guān)鍵區(qū)域中出現(xiàn)失敗時,宿主可能決定卸載整個AppDomain,而不是冒險在可能不穩(wěn)定的狀態(tài)下繼續(xù)執(zhí)行。
例如,假設(shè)有一個嘗試在占有鎖時分配內(nèi)存的任務(wù)。如果內(nèi)存分配失敗,則中止當(dāng)前任務(wù)并不足以確保AppDomain的穩(wěn)定性,原因是域中可能存在其他等待同一個鎖的任務(wù)。如果終止當(dāng)前任務(wù),則可能導(dǎo)致其他任務(wù)死鎖。
11.???? BeginThreadAffinity()+EndThreadAffinity()?? (Affinity:喜愛,密切關(guān)系)
使用BeginThreadAffinity和EndThreadAffinity方法通知宿主代碼塊依賴于物理操作系統(tǒng)線程的標(biāo)識。
公共語言運(yùn)行庫的某些宿主提供其自己的線程管理。提供其自己的線程管理的宿主可以在任何時候?qū)⒄趫?zhí)行的任務(wù)從一個物理操作系統(tǒng)線程移至另一個物理操作系統(tǒng)線程。大多數(shù)任務(wù)不會受此切換影響。但是,某些任務(wù)具有【線程關(guān)聯(lián)】 -- 即它們依賴于物理操作系統(tǒng)線程的標(biāo)識。這些任務(wù)在其執(zhí)行“不應(yīng)被切換的代碼”時必須通知宿主。
例如,如果應(yīng)用程序調(diào)用系統(tǒng) API 以獲取具有【線程關(guān)聯(lián)】的操作系統(tǒng)鎖(如 Win32 CRITICAL_SECTION),則必須在獲取該鎖之前調(diào)用BeginThreadAffinity,并在釋放該鎖之后調(diào)用EndThreadAffinity。
還必須在從WaitHandle繼承的任何 .NET Framework 類型上發(fā)生阻止之前調(diào)用BeginThreadAffinity,因?yàn)檫@些類型依賴于操作系統(tǒng)對象。
?
線程本地存儲區(qū)和線程相關(guān)的靜態(tài)字段
可以使用托管線程本地存儲區(qū) (TLS,Thread-Local Storage) 和線程相關(guān)的靜態(tài)字段來存儲某一線程和應(yīng)用程序域所獨(dú)有的數(shù)據(jù)。
a)???????? 如果可以在編譯時預(yù)料到確切需要,請使用線程相關(guān)的靜態(tài)字段。
b)???????? 如果只能在運(yùn)行時發(fā)現(xiàn)實(shí)際需要,請使用數(shù)據(jù)槽。
為了獲得更好的性能,請盡量改用以 System.ThreadStaticAttribute特性標(biāo)記的線程相關(guān)的靜態(tài)字段。
無論是使用線程相關(guān)的靜態(tài)字段還是使用數(shù)據(jù)槽,托管 TLS 中的數(shù)據(jù)都是線程和應(yīng)用程序域組合所獨(dú)有的。
a)???????? 在應(yīng)用程序域內(nèi)部,一個線程不能修改另一個線程中的數(shù)據(jù),即使這兩個線程使用同一個字段或槽時也不能。
b)???????? 當(dāng)線程從多個應(yīng)用程序域中訪問同一個字段或槽時,會在每個應(yīng)用程序域中維護(hù)一個單獨(dú)的值。
1)???????? 線程相關(guān)的靜態(tài)字段(編譯時)
如果您知道某類型的字段【總是某個線程和應(yīng)用程序域組合】所獨(dú)有的(即不是共享的),則使用ThreadStaticAttribute修飾靜態(tài)字段(static)。
需要注意的是,任何類構(gòu)造函數(shù)代碼都將在訪問該字段的第一個上下文中的第一個線程上運(yùn)行。在所有其他線程或上下文中,如果這些字段是引用類型,將被初始化為 null;如果這些字段是值類型,將被初始化為它們的默認(rèn)值。因此,不要依賴于類構(gòu)造函數(shù)來初始化線程相關(guān)的靜態(tài)字段[ThreadStatic]。相反,應(yīng)總是假定與線程相關(guān)的靜態(tài)字段被初始化為 null 或它們的默認(rèn)值。
2)???????? 數(shù)據(jù)槽(運(yùn)行時)
?????????見上一小節(jié)(線程Thread類詳解)第8點(diǎn)分析
?
???????? 示例:托管TSL中數(shù)據(jù)的唯一性(數(shù)據(jù)槽|線程相關(guān)靜態(tà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 | /// <summary> /// 數(shù)據(jù)槽? 的使用示例 /// </summary> private?static?void?TLS4DataSlot() { ????LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("Name"); ????Console.WriteLine(String.Format("ID為{0}的線程,命名為\"Name\"的數(shù)據(jù)槽,開始設(shè)置數(shù)據(jù)。", Thread.CurrentThread.ManagedThreadId)); ????Thread.SetData(slot, "小麗"); ????Console.WriteLine(String.Format("ID為{0}的線程,命名為\"Name\"的數(shù)據(jù)槽,數(shù)據(jù)是\"{1}\"。" ?????????????????????, Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot))); ? ????Thread newThread = new?Thread( ????????() => ????????{ ????????????LocalDataStoreSlot storeSlot = Thread.GetNamedDataSlot("Name"); ????????????Console.WriteLine(String.Format("ID為{0}的線程,命名為\"Name\"的數(shù)據(jù)槽,在新線程為其設(shè)置數(shù)據(jù) 前 為\"{1}\"。" ?????????????????????????????, Thread.CurrentThread.ManagedThreadId, Thread.GetData(storeSlot))); ????????????Console.WriteLine(String.Format("ID為{0}的線程,命名為\"Name\"的數(shù)據(jù)槽,開始設(shè)置數(shù)據(jù)。", Thread.CurrentThread.ManagedThreadId)); ????????????Thread.SetData(storeSlot, "小紅"); ????????????Console.WriteLine(String.Format("ID為{0}的線程,命名為\"Name\"的數(shù)據(jù)槽,在新線程為其設(shè)置數(shù)據(jù) 后 為\"{1}\"。" ?????????????????????????????, Thread.CurrentThread.ManagedThreadId, Thread.GetData(storeSlot))); ? ????????????// 命名數(shù)據(jù)槽中分配的數(shù)據(jù)必須用 FreeNamedDataSlot() 釋放。未命名的數(shù)據(jù)槽數(shù)據(jù)隨線程的銷毀而釋放 ????????????Thread.FreeNamedDataSlot("Name"); ????????} ????); ????newThread.Start(); ????newThread.Join(); ? ????Console.WriteLine(String.Format("執(zhí)行完新線程后,ID為{0}的線程,命名為\"Name\"的數(shù)據(jù)槽,在新線程為其設(shè)置數(shù)據(jù) 后 為\"{1}\"。" ?????????????????????, Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot))); } | 
?
?
| 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 | // 不應(yīng)依賴于類構(gòu)造函數(shù)來初始化線程相關(guān)的靜態(tài)字段[ThreadStatic] [ThreadStatic] static?string?name = String.Empty; /// <summary> /// 線程相關(guān)靜態(tài)字段? 的使用示例 /// </summary> private?static?void?TLS4StaticField() { ????Console.WriteLine(String.Format("ID為{0}的線程,開始為name靜態(tài)字段設(shè)置數(shù)據(jù)。", Thread.CurrentThread.ManagedThreadId)); ????name = "小麗"; ????Console.WriteLine(String.Format("ID為{0}的線程,name靜態(tài)字段數(shù)據(jù)為\"{1}\"。", Thread.CurrentThread.ManagedThreadId, name)); ? ????Thread newThread = new?Thread( ????????() => ????????{ ????????????Console.WriteLine(String.Format("ID為{0}的線程,為name靜態(tài)字段設(shè)置數(shù)據(jù) 前 為\"{1}\"。", Thread.CurrentThread.ManagedThreadId, name)); ????????????Console.WriteLine(String.Format("ID為{0}的線程,開始為name靜態(tài)字段設(shè)置數(shù)據(jù)。", Thread.CurrentThread.ManagedThreadId)); ????????????name = "小紅"; ????????????Console.WriteLine(String.Format("ID為{0}的線程,為name靜態(tài)字段設(shè)置數(shù)據(jù) 后 為\"{1}\"。", Thread.CurrentThread.ManagedThreadId, name)); ????????} ????); ????newThread.Start(); ????newThread.Join(); ? ????Console.WriteLine(String.Format("執(zhí)行完新線程后,ID為{0}的線程,name靜態(tài)字段數(shù)據(jù)為\"{1}\"。", Thread.CurrentThread.ManagedThreadId, name)); } | 
?
??????? 結(jié)果截圖:
????
?
.NET下未捕獲異常的處理
1.?????????控制臺應(yīng)用程序
?????????通過為當(dāng)前AppDomain添加?UnhandledException?事件處理程序。
| 1 2 3 | AppDomain.CurrentDomain.UnhandledException += ???????????????new?UnhandledExceptionEventHandler(UnhandledExceptionEventHandler); static?void?UnhandledExceptionEventHandler(object?sender, UnhandledExceptionEventArgs e)? { …… } | 
2.?????????WinForm窗體應(yīng)用程序
未處理的異常將引發(fā)Application.ThreadException事件。
a)?????????如果異常發(fā)生在主線程中,默認(rèn)行為是未經(jīng)處理的異常不終止該應(yīng)用程序。在這種情況下,不會引發(fā)?UnhandledException?事件。但可以在在掛鉤?ThreadException?事件處理程序之前,使用應(yīng)用程序配置文件或者使用?Application.SetUnhandledExceptionMode()?方法將模式設(shè)置為?UnhandledExceptionMode.ThrowException?來更改此默認(rèn)行為。
b)?????????如果異常發(fā)生在其它線程中,將引發(fā)?UnhandledException?事件。
| 1 2 | Application.ThreadException += new?ThreadExceptionEventHandler(Application_ThreadException) static?void?Application_ThreadException(object?sender, ThreadExceptionEventArgs e)? { …… } | 
3.?????????ASP.NET應(yīng)用程序
要截獲ASP.NET?的未捕獲異常,我們需要為每個應(yīng)用程序域安裝事件鉤子。這個過程需要分兩步完成:
a)?????????首先創(chuàng)建一個實(shí)現(xiàn)IHttpModule接口的類
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public?class?UnhandledExceptionModule : IHttpModule { ????…… ????static?object?_initLock = new?object(); ????static?bool?_initialized = false; ????public?void?Init(HttpApplication context) ????{ ????????// Do this one time for each AppDomain. ????????lock?(_initLock) ????????{ ????????????if?(!_initialized) ????????????{ ????????????????AppDomain.CurrentDomain.UnhandledException += new?UnhandledExceptionEventHandler(OnUnhandledException); ????????????????_initialized = true; ????????????} ????????} ????} } | 
b)?????????第二步:修改web.config,在?system.web?段中加入
| 1 2 3 | <httpModules> ??<add name="UnhandledExceptionModule"?type="WebMonitor.UnhandledExceptionModule"?/> </httpModules> | 
判斷多個線程是否都結(jié)束的幾種方法
有園友問到此問題,所以提供下面幾種方法。若還有其他方法請告知。
1.???????? 線程計數(shù)器
????? 線程也可以采用計數(shù)器的方法,即為所有需要監(jiān)視的線程設(shè)一個線程計數(shù)器,每開始一個線程,在線程的執(zhí)行方法中為這個計數(shù)器加1,如果某個線程結(jié)束(在線程執(zhí)行方法的最后),為這個計數(shù)器減1。使用這種方法需要使用原子操作(eg:Volatile、InterLocked)同步這個計數(shù)器變量。
2.???????? 使用Thread.join方法
join方法只有在線程結(jié)束時才繼續(xù)執(zhí)行下面的語句。可以對每一個線程調(diào)用它的join方法,但要注意,這個調(diào)用要在一個專門線程里做,而不要在主線程,否則程序會被阻塞。
3.???????? 輪詢Thread的IsAlive屬性
???? IsAlive判斷此線程是否還存活。經(jīng)測試只有 Unstarted、Stopped 返回false;其他線程狀態(tài)都返回true。
???? 我們通過輪詢檢查此屬性來判斷線程是否結(jié)束。但要注意,這個調(diào)用要在一個專門線程里做,而不要在主線程,否則程序會被阻塞。
????????? EG:while(true) { foreach(多個線程){ if(thread1.IsAlive) { } } }
4.???????? 使用回調(diào)函數(shù)進(jìn)行通知
請參考“線程Thread類詳解”節(jié)第二點(diǎn)示例
5.???????? 使用同步基元對象
???? Eg:WaitHandle。在后續(xù)章節(jié)中再說明
?
?
?
本博文主要為大家介紹了進(jìn)程和線程的差別,計算機(jī)對多線程的支持,Thread類的詳解,線程狀態(tài)及影響線程狀態(tài)的各種線程操作,托管線程本地存儲區(qū),線程中未處理異常的捕獲等等……
看完后你會發(fā)現(xiàn)如果程序任務(wù)小而多會造成不斷的創(chuàng)建和銷毀線程不便于線程管理;你可能還會發(fā)現(xiàn)當(dāng)線程操作共享資源的時候沒有控制資源的同步問題……在后續(xù)章節(jié)中會陸續(xù)引入線程池和同步基元對象解決相應(yīng)問題,敬請查看。
????? 本節(jié)就此結(jié)束,謝謝大家查看,一起學(xué)習(xí)一起進(jìn)步。
?
?
?
參考資料
???????????????????MSDN
擴(kuò)展知識:
??????????????????Microsoft Windows 中的托管和非托管線程處理
??????????????????多核編程偽共享問題及其對策
?
總結(jié)
以上是生活随笔為你收集整理的【转】1.1异步编程:线程概述及使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 这次更新还稍微有点用!微信“群聊消息置顶
- 下一篇: 随随便便捡个小钱,一年能拣上千元,你也可
