国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

c# 多線程-異步-WaitHandle-AutoResetEvent

2019-11-11 03:29:19
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

先表明,向作者致敬http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html 風(fēng)塵浪子 前半部分是復(fù)制風(fēng)塵浪子的,從 三 開(kāi)始,互聯(lián)網(wǎng)收集整理. 感謝互聯(lián)網(wǎng),感謝open source. 重要是,大家能夠領(lǐng)悟,掌握和運(yùn)用多線程的知識(shí).

一、線程的定義

1. 1 進(jìn)程、應(yīng)用程序域與線程的關(guān)系 進(jìn)程(PRocess)是Windows系統(tǒng)中的一個(gè)基本概念,它包含著一個(gè)運(yùn)行程序所需要的資源。進(jìn)程之間是相對(duì)獨(dú)立的,一個(gè)進(jìn)程無(wú)法訪問(wèn)另一個(gè) 進(jìn)程的數(shù)據(jù)(除非利用分布式計(jì)算方式),一個(gè)進(jìn)程運(yùn)行的失敗也不會(huì)影響其他進(jìn)程的運(yùn)行,Windows系統(tǒng)就是利用進(jìn)程把工作劃分為多個(gè)獨(dú)立的區(qū)域的。進(jìn) 程可以理解為一個(gè)程序的基本邊界。 應(yīng)用程序域(AppDomain)是一個(gè)程序運(yùn)行的邏輯區(qū)域,它可以視為一個(gè)輕量級(jí)的進(jìn)程,.NET的程序集正是在應(yīng)用程序域中運(yùn)行的,一個(gè)進(jìn)程可 以包含有多個(gè)應(yīng)用程序域,一個(gè)應(yīng)用程序域也可以包含多個(gè)程序集。在一個(gè)應(yīng)用程序域中包含了一個(gè)或多個(gè)上下文context,使用上下文CLR就能夠把某些 特殊對(duì)象的狀態(tài)放置在不同容器當(dāng)中。 線程(Thread)是進(jìn)程中的基本執(zhí)行單元,在進(jìn)程入口執(zhí)行的第一個(gè)線程被視為這個(gè)進(jìn)程的主線程。在.NET應(yīng)用程序中,都是以Main()方法 作為入口的,當(dāng)調(diào)用此方法時(shí)系統(tǒng)就會(huì)自動(dòng)創(chuàng)建一個(gè)主線程。線程主要是由CPU寄存器、調(diào)用棧和線程本地存儲(chǔ)器(Thread Local Storage,TLS)組成的。CPU寄存器主要記錄當(dāng)前所執(zhí)行線程的狀態(tài),調(diào)用棧主要用于維護(hù)線程所調(diào)用到的內(nèi)存與數(shù)據(jù),TLS主要用于存放線程的狀 態(tài)信息。 進(jìn)程、應(yīng)用程序域、線程的關(guān)系如下圖,一個(gè)進(jìn)程內(nèi)可以包括多個(gè)應(yīng)用程序域,也有包括多個(gè)線程,線程也可以穿梭于多個(gè)應(yīng)用程序域當(dāng)中。但在同一個(gè)時(shí)刻,線程只會(huì)處于一個(gè)應(yīng)用程序域內(nèi)。

1.2 多線程

在單CPU系統(tǒng)的一個(gè)單位時(shí)間(time slice)內(nèi),CPU只能運(yùn)行單個(gè)線程,運(yùn)行順序取決于線程的優(yōu)先級(jí)別。如果在單位時(shí)間內(nèi)線程未能完成執(zhí)行,系統(tǒng)就會(huì)把線程的狀態(tài)信息保存到線程的本地 存儲(chǔ)器(TLS) 中,以便下次執(zhí)行時(shí)恢復(fù)執(zhí)行。而多線程只是系統(tǒng)帶來(lái)的一個(gè)假像,它在多個(gè)單位時(shí)間內(nèi)進(jìn)行多個(gè)線程的切換。因?yàn)榍袚Q頻密而且單位時(shí)間非常短暫,所以多線程可 被視作同時(shí)運(yùn)行。 適當(dāng)使用多線程能提高系統(tǒng)的性能,比如:在系統(tǒng)請(qǐng)求大容量的數(shù)據(jù)時(shí)使用多線程,把數(shù)據(jù)輸出工作交給異步線程,使主線程保持其穩(wěn)定性去處理其他問(wèn)題。但需要注意一點(diǎn),因?yàn)镃PU需要花費(fèi)不少的時(shí)間在線程的切換上,所以過(guò)多地使用多線程反而會(huì)導(dǎo)致性能的下降。

二、線程的基礎(chǔ)知識(shí)

2.1 System.Threading.Thread類(lèi) System.Threading.Thread是用于控制線程的基礎(chǔ)類(lèi),通過(guò)Thread可以控制當(dāng)前應(yīng)用程序域中線程的創(chuàng)建、掛起、停止、銷(xiāo)毀。 它包括以下常用公共屬性:

屬性名稱 說(shuō)明
CurrentContext 獲取線程正在其中執(zhí)行的當(dāng)前上下文。
CurrentThread 獲取當(dāng)前正在運(yùn)行的線程。
ExecutionContext 獲取一個(gè) ExecutionContext 對(duì)象,該對(duì)象包含有關(guān)當(dāng)前線程的各種上下文的信息。
IsAlive 獲取一個(gè)值,該值指示當(dāng)前線程的執(zhí)行狀態(tài)。
IsBackground 獲取或設(shè)置一個(gè)值,該值指示某個(gè)線程是否為后臺(tái)線程。
IsThreadPoolThread 獲取一個(gè)值,該值指示線程是否屬于托管線程池。
ManagedThreadId 獲取當(dāng)前托管線程的唯一標(biāo)識(shí)符。
Name 獲取或設(shè)置線程的名稱。
Priority 獲取或設(shè)置一個(gè)值,該值指示線程的調(diào)度優(yōu)先級(jí)。
ThreadState 獲取一個(gè)值,該值包含當(dāng)前線程的狀態(tài)。

2.1.1 線程的標(biāo)識(shí)符 ManagedThreadId是確認(rèn)線程的唯一標(biāo)識(shí)符,程序在大部分情況下都是通過(guò)Thread.ManagedThreadId來(lái)辨別線程的。 而Name是一個(gè)可變值,在默認(rèn)時(shí)候,Name為一個(gè)空值 Null,開(kāi)發(fā)人員可以通過(guò)程序設(shè)置線程的名稱,但這只是一個(gè)輔助功能。

2.1.2 線程的優(yōu)先級(jí)別 .NET為線程設(shè)置了Priority屬性來(lái)定義線程執(zhí)行的優(yōu)先級(jí)別,里面包含5個(gè)選項(xiàng),其中Normal是默認(rèn)值。除非系統(tǒng)有特殊要求,否則不應(yīng)該隨便設(shè)置線程的優(yōu)先級(jí)別。

成員名稱 說(shuō)明
Lowest 可以將 Thread 安排在具有任何其他優(yōu)先級(jí)的線程之后。
BelowNormal 可以將 Thread 安排在具有 Normal 優(yōu)先級(jí)的線程之后,在具有 Lowest 優(yōu)先級(jí)的線程之前。
Normal 默認(rèn)選擇。可以將 Thread 安排在具有 AboveNormal 優(yōu)先級(jí)的線程之后,在具有 BelowNormal 優(yōu)先級(jí)的線程之前。
AboveNormal 可以將 Thread 安排在具有 Highest 優(yōu)先級(jí)的線程之后,在具有 Normal 優(yōu)先級(jí)的線程之前。
Highest 可以將 Thread 安排在具有任何其他優(yōu)先級(jí)的線程之前。

2.1.3 線程的狀態(tài) 通過(guò)ThreadState可以檢測(cè)線程是處于Unstarted、Sleeping、Running 等等狀態(tài),它比 IsAlive 屬性能提供更多的特定信息。 前面說(shuō)過(guò),一個(gè)應(yīng)用程序域中可能包括多個(gè)上下文,而通過(guò)CurrentContext可以獲取線程當(dāng)前的上下文。 CurrentThread是最常用的一個(gè)屬性,它是用于獲取當(dāng)前運(yùn)行的線程。

2.1.4 System.Threading.Thread的方法 Thread 中包括了多個(gè)方法來(lái)控制線程的創(chuàng)建、掛起、停止、銷(xiāo)毀,以后來(lái)的例子中會(huì)經(jīng)常使用。

方法名稱 說(shuō)明
Abort() 終止本線程。
GetDomain() 返回當(dāng)前線程正在其中運(yùn)行的當(dāng)前域。
GetDomainId() 返回當(dāng)前線程正在其中運(yùn)行的當(dāng)前域Id。
Interrupt() 中斷處于 WaitSleepJoin 線程狀態(tài)的線程。
Join() 已重載。 阻塞調(diào)用線程,直到某個(gè)線程終止時(shí)為止。
Resume() 繼續(xù)運(yùn)行已掛起的線程。
Start() 執(zhí)行本線程。
Suspend() 掛起當(dāng)前線程,如果當(dāng)前線程已屬于掛起狀態(tài)則此不起作用
Sleep() 把正在運(yùn)行的線程掛起一段時(shí)間。

2.1.5 開(kāi)發(fā)實(shí)例 以下這個(gè)例子,就是通過(guò)Thread顯示當(dāng)前線程信息

static void Main(string[] args) { Thread thread = Thread.CurrentThread; thread.Name = "Main Thread"; string threadMessage = string.Format("Thread ID:{0}/n Current AppDomainId:{1}/n " + "Current ContextId:{2}/n Thread Name:{3}/n " + "Thread State:{4}/n Thread Priority:{5}/n", thread.ManagedThreadId, Thread.GetDomainID(), Thread.CurrentContext.ContextID, thread.Name, thread.ThreadState, thread.Priority); Console.WriteLine(threadMessage); Console.ReadKey(); }

這里寫(xiě)圖片描述

三、以ThreadStart方式實(shí)現(xiàn)多線程

3.1使用ThreadStart

static void Main(string[] args) { //Thread th = new Thread(Sleep); Thread th = new Thread(new ThreadStart(Sleep)); th.Start(); for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } } //模擬執(zhí)行長(zhǎng)時(shí)間的任務(wù) static void Sleep() { ThreadMessage("Sleep"); Console.WriteLine("我還沒(méi)有執(zhí)行完呢,請(qǐng)耐心等待...."); Thread.Sleep(3000); } static void ThreadMessage(string data) { string message = string.Format("ThreadName is {0} ThreadId is:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); }

這里寫(xiě)圖片描述 運(yùn)行后你會(huì)發(fā)現(xiàn),Sleep()方法 還沒(méi)有執(zhí)行完(睡眠3分鐘模擬長(zhǎng)時(shí)間任務(wù)),主線程已經(jīng)執(zhí)行完成. 這就是多線程的好處. 3.2 ParameterizedThreadStart 帶參數(shù)的

static void Main(string[] args) { //Thread th = new Thread(Sleep); Thread th = new Thread(new ParameterizedThreadStart(Sleep)); th.Start("Sleep"); for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } } //模擬執(zhí)行長(zhǎng)時(shí)間的任務(wù) static void Sleep(object state) { string name = (string)state; ThreadMessage(name); Console.WriteLine("我還沒(méi)有執(zhí)行完呢,請(qǐng)耐心等待...."); Thread.Sleep(3000); } static void ThreadMessage(string data) { string message = string.Format("ThreadName is {0} ThreadId is:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); }

這里寫(xiě)圖片描述 運(yùn)行結(jié)果和上面那差不多一樣.

3.3 匿名函數(shù)(委托)在多線程的運(yùn)用 想必大家發(fā)現(xiàn)了這2個(gè)例子中都有一句注釋的Thread th = new Thread(Sleep),這是為什么呢? 你可以把注釋打開(kāi),下面的th注釋掉,運(yùn)行下,你會(huì)發(fā)現(xiàn),兩次都一樣,這又是為什么呢?因?yàn)樵趯?shí)例化th的時(shí)候,有4個(gè)重載方法,其中2個(gè)就是ThreadStart和ParameterizedThreadStart 這里寫(xiě)圖片描述 我們都知道ThreadStart和ParameterizedThreadStart和2個(gè)委托,而委托最大的作用就是傳遞一個(gè)方法, 在C#2.0就引入了匿名方法(3.0以及更高,lambda表達(dá)式取代了匿名方法)我們來(lái)看看匿名方法在這里能給我們帶來(lái)點(diǎn)什么驚喜(方便)

class Program { static void Main(string[] args) { //Thread th = new Thread(delegate() { //在new ThreadStart()中 // ShowMessage("ShowMessage"); //}); //Thread th = new Thread(new ThreadStart(delegate() { // ShowMessage("ShowMessage"); //})); //Thread th = new Thread(new ThreadStart(() => //{ // ShowMessage("ShowMessage"); //})); Thread th = new Thread(delegate(object o) //new ParameterizedThreadStart()中的 { ShowMessage("ShowMessage"); }); //Thread th = new Thread(new ParameterizedThreadStart(delegate(object o) { // ShowMessage("ShowMessage"); //})); //Thread th = new Thread(t => ShowMessage("show")); th.Start(); for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } } //模擬執(zhí)行長(zhǎng)時(shí)間的任務(wù) static void Sleep(object state) { string name = (string)state; ThreadMessage(name); Console.WriteLine("我還沒(méi)有執(zhí)行完呢,請(qǐng)耐心等待...."); Thread.Sleep(3000); } //這里的參數(shù)不為object,你可以定義任何類(lèi)型,任何參數(shù) static void ShowMessage(string message) { ThreadMessage(message); Console.WriteLine("我還沒(méi)有執(zhí)行完呢,請(qǐng)耐心等待...."); Thread.Sleep(3000); } static void ThreadMessage(string data) { string message = string.Format("ThreadName is {0} ThreadId is:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); } }}

這里寫(xiě)圖片描述 上面的幾種方式都可以用. 我為什么要用個(gè)ShowMessage(string msg) 方法來(lái)測(cè)試呢 ? 細(xì)心你會(huì)發(fā)現(xiàn)ParameterizedThreadStart委托他定義的參數(shù)為object,用匿名函數(shù)就可以解決這問(wèn)題.如果你對(duì)委托,匿名函數(shù)不太熟悉的話,你就要補(bǔ)習(xí)一下關(guān)于委托的知識(shí)了.

3.4 前臺(tái)線程and后臺(tái)線程 注意以上兩個(gè)例子都沒(méi)有使用Console.ReadKey(),但系統(tǒng)依然會(huì)等待異步線程完成后才會(huì)結(jié)束。這是因?yàn)槭褂肨hread.Start()啟動(dòng)的線程默認(rèn)為前臺(tái)線程,而系統(tǒng)必須等待所有前臺(tái)線程運(yùn)行結(jié)束后,應(yīng)用程序域才會(huì)自動(dòng)卸載。 在第二節(jié)曾經(jīng)介紹過(guò)線程Thread有一個(gè)屬性IsBackground,通過(guò)把此屬性設(shè)置為true,就可以把線程設(shè)置為后臺(tái)線程!這時(shí)應(yīng)用程序域?qū)⒃谥骶€程完成時(shí)就被卸載(主線程關(guān)閉),而不會(huì)等待異步線程的運(yùn)行。

3.5 線程的一些方法 Thread.Sleep()大家都很熟悉了,休眠多長(zhǎng)時(shí)間,里面是毫秒.1秒=1000毫秒. Join() 表面意思是把線程加入,也就是這線程完事后主線程才被卸載. 你可以把子線程設(shè)置成后臺(tái)線程,然后調(diào)用這個(gè)方法,就不會(huì)發(fā)現(xiàn)’一閃而過(guò)’的現(xiàn)象了. Thread.Suspend()與 Thread.Resume()是在Framework1.0 就已經(jīng)存在的老方法了,它們分別可以掛起、恢復(fù)線程。但在Framework2.0中就已經(jīng)明確排斥這兩個(gè)方法。這是因?yàn)橐坏┠硞€(gè)線程占用了已有的資源,再使用Suspend()使線程長(zhǎng)期處于掛起狀態(tài),當(dāng)在其他線程調(diào)用這些資源的時(shí)候就會(huì)引起死鎖!所以在沒(méi)有必要的情況下應(yīng)該避免使用這兩個(gè)方法。 若想終止正在運(yùn)行的線程,可以使用Abort()方法。在使用Abort()的時(shí)候,將引發(fā)一個(gè)特殊異常 ThreadAbortException 。 若想在線程終止前恢復(fù)線程的執(zhí)行,可以在捕獲異常后 ,在catch(ThreadAbortException ex){…} 中調(diào)用Thread.ResetAbort()取消終止。 下面的例子是 終止線程和取消終止的例子(拷貝風(fēng)塵浪子的)

class Program { static void Main(string[] args) { Console.WriteLine("Main threadId is:" + Thread.CurrentThread.ManagedThreadId); Thread thread = new Thread(new ThreadStart(AsyncThread)); thread.Start(); Console.ReadKey(); } //以異步方式調(diào)用 static void AsyncThread() { try { string message = string.Format("/nAsync threadId is:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); for (int n = 0; n < 10; n++) { //當(dāng)n等于4時(shí),終止線程 if (n >= 4) { Thread.CurrentThread.Abort(n); } Thread.Sleep(300); Console.WriteLine("The number is:" + n.ToString()); } } catch (ThreadAbortException ex) { //輸出終止線程時(shí)n的值 if (ex.ExceptionState != null) Console.WriteLine(string.Format("Thread abort when the number is: {0}!", ex.ExceptionState.ToString())); //取消終止,繼續(xù)執(zhí)行線程 Thread.ResetAbort(); Console.WriteLine("Thread ResetAbort!"); } //線程結(jié)束 Console.WriteLine("Thread Close!"); } }

這里寫(xiě)圖片描述

到此,我們學(xué)會(huì)了運(yùn)用多線程,可不能沾沾自喜,這可只是剛剛開(kāi)始. 前面說(shuō)了通過(guò)ThreadStart創(chuàng)建的線程比較難管理,創(chuàng)建過(guò)多性能也會(huì)下降.主要是因?yàn)門(mén)hreadStart創(chuàng)建的線程不能循環(huán)利用,比如我們For循環(huán)個(gè)list,每一個(gè)model都開(kāi)啟個(gè)線程去執(zhí)行任務(wù),當(dāng)前面的線程執(zhí)行完了,也就銷(xiāo)毀了,后面的還是要重新創(chuàng)建,不停的創(chuàng)建線程是很耗時(shí)的.由此可見(jiàn) .NET為線程管理專門(mén)設(shè)置了一個(gè)CLR線程池.

四、CLR線程池的工作者線程

4.1 關(guān)于CLR線程池 使用ThreadStart與ParameterizedThreadStart建立新線程非常簡(jiǎn)單,但通過(guò)此方法建立的線程難于管理,若建立過(guò)多的線程反而會(huì)影響系統(tǒng)的性能。 有 見(jiàn)及此,.NET引入CLR線程池這個(gè)概念。CLR線程池并不會(huì)在CLR初始化的時(shí)候立刻建立線程,而是在應(yīng)用程序要?jiǎng)?chuàng)建線程來(lái)執(zhí)行任務(wù)時(shí),線程池才初始 化一個(gè)線程。線程的初始化與其他的線程一樣。在完成任務(wù)以后,該線程不會(huì)自行銷(xiāo)毀,而是以掛起的狀態(tài)返回到線程池。直到應(yīng)用程序再次向線程池發(fā)出請(qǐng)求時(shí), 線程池里掛起的線程就會(huì)再度激活執(zhí)行任務(wù)。這樣既節(jié)省了建立線程所造成的性能損耗,也可以讓多個(gè)任務(wù)反復(fù)重用同一線程,從而在應(yīng)用程序生存期內(nèi)節(jié)約大量開(kāi)銷(xiāo). 4.2 工作者線程與I/O線程

CLR線程池分為工作者線程(workerThreads)與I/O線程 (completionPortThreads) 兩種,工作者線程是主要用作管理CLR內(nèi)部對(duì)象的運(yùn)作,I/O(Input/Output) 線程顧名思義是用于與外部系統(tǒng)交換信息,IO線程的細(xì)節(jié)將在下一節(jié)詳細(xì)說(shuō)明。

通過(guò)ThreadPool.GetMax(out int workerThreads,out int completionPortThreads )和 ThreadPool.SetMax( int workerThreads, int completionPortThreads)兩個(gè)方法可以分別讀取和設(shè)置CLR線程池中工作者線程與I/O線程的最大線程數(shù)。在 Framework2.0中最大線程默認(rèn)為25*CPU數(shù),在Framewok3.0、4.0中最大線程數(shù)默認(rèn)為250*CPU數(shù),在近年 I3,I5,I7 CPU出現(xiàn)后,線程池的最大值一般默認(rèn)為1000、2000。 若想測(cè)試線程池中有多少的線程正在投入使用,可以通過(guò)ThreadPool.GetAvailableThreads( out int workerThreads,out int completionPortThreads ) 方法。

使用CLR線程池的工作者線程一般有兩種方式,一是直接通過(guò) ThreadPool.QueueUserWorkItem() 方法,二是通過(guò)委托(異步操作是加入在線程池中的),下面將逐一細(xì)說(shuō)。 4.3 通過(guò)QueueUserWorkItem啟動(dòng)工作者線程 ThreadPool線程池中包含有兩個(gè)靜態(tài)方法可以直接啟動(dòng)工作者線程: 一為 ThreadPool.QueueUserWorkItem(WaitCallback) 二為 ThreadPool.QueueUserWorkItem(WaitCallback,Object) 先把WaitCallback委托指向一個(gè)帶有Object參數(shù)的無(wú)返回值方法,再使用 ThreadPool.QueueUserWorkItem(WaitCallback) 就可以異步啟動(dòng)此方法,此時(shí)異步方法的參數(shù)被視為null 。

class Program { static void Main(string[] args) { ThreadPool.QueueUserWorkItem(new WaitCallback(Sleep)); for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } Console.ReadKey(); } static void Sleep(object state) { ThreadMessage("sleep"); Console.WriteLine("我還沒(méi)有執(zhí)行完呢,請(qǐng)耐心等待...."); Thread.Sleep(3000); } static void ThreadMessage(string data) { string message = string.Format("ThreadName is {0} ThreadId is:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); } }

這里寫(xiě)圖片描述 這是個(gè)不帶參數(shù)的, ThreadPool.QueueUserWorkItem()有2個(gè)參數(shù),第二個(gè)是個(gè)object類(lèi)型

class Program { static void Main(string[] args) { Person person = new Person() { Name = "Somnus", Age = 10 }; ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), person); for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } Console.ReadKey(); } static void ShowMessage(object state) { Person person = (Person)state; Console.WriteLine("學(xué)生姓名是{0},年齡為{1}", person.Name, person.Age); Thread.Sleep(3000); } } class Person { public string Name { get; set; } public int Age { get; set; } //public WaitHandle Wait { get; set; } }

這里寫(xiě)圖片描述 我們創(chuàng)建了一個(gè)Person類(lèi),在加入線程池的時(shí)候,我們傳了一個(gè)person , 在ShowMessage()方法中,得到傳過(guò)來(lái)的person,并顯示人的信息. 那在這里,我們可以效仿3.3匿名函數(shù)在線程池中的應(yīng)用呢? 答案是可以的. 4.4 線程池中的異常處理 多線程執(zhí)行任務(wù)的時(shí)候,有時(shí)候總是要出錯(cuò)嘛, 重要的是,我們能catch的住異常.

class Program { static void Main(string[] args) { Person person = new Person() { Name = "Somnus", Age = 10 }; try { ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), person); } catch (Exception ex) { //寫(xiě)日志 } for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } Console.ReadKey(); } static void ShowMessage(object state) { Person person = (Person)state; throw new Exception("這里出錯(cuò)了,快catch住我"); Console.WriteLine("學(xué)生姓名是{0},年齡為{1}", person.Name, person.Age); Thread.Sleep(3000); } } class Person { public string Name { get; set; } public int Age { get; set; } //public WaitHandle Wait { get; set; } }

這里寫(xiě)圖片描述 這樣? No~ 結(jié)果會(huì)差強(qiáng)人意的,我們并沒(méi)有catch住他. 那該在哪try呢. 對(duì), 在委托調(diào)用那方法中. 我們這樣干,

class Program { static void Main(string[] args) { Person person = new Person() { Name = "Somnus", Age = 10 }; ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), person); for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } Console.ReadKey(); } static void ShowMessage(object state) { try { Person person = (Person)state; throw new Exception("這里出錯(cuò)了,快catch住我"); Console.WriteLine("學(xué)生姓名是{0},年齡為{1}", person.Name, person.Age); Thread.Sleep(3000); } catch (Exception ex) { //寫(xiě)日志 } } } class Person { public string Name { get; set; } public int Age { get; set; } //public WaitHandle Wait { get; set; } }

這里寫(xiě)圖片描述 這樣,程序就不會(huì)報(bào)錯(cuò)了. 如果有好幾個(gè)任務(wù)方法, 那我們要寫(xiě)好幾個(gè)try{}catch{}這樣肯定不是我們想要的,看看下面的封裝

class Program { static void Main(string[] args) { Person person = new Person() { Name = "Somnus", Age = 10 }; ThreadExecutor.Execute(new WaitCallback(ShowMessage), person); for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } Console.ReadKey(); } static void ShowMessage(object state) { Person person = (Person)state; throw new Exception("這里出錯(cuò)了,快catch住我"); Console.WriteLine("學(xué)生姓名是{0},年齡為{1}", person.Name, person.Age); Thread.Sleep(3000); } } class Person { public string Name { get; set; } public int Age { get; set; } //public WaitHandle Wait { get; set; } } public class ThreadExecutor { public static bool Execute(System.Threading.WaitCallback callback, object state) { try { return System.Threading.ThreadPool.QueueUserWorkItem((data) => { try { callback(data); } catch (Exception ex) { //寫(xiě)日志 } }, state); } catch (Exception e) { //寫(xiě)日志 } return false; } public static bool Execute(System.Threading.WaitCallback callback) { try { return System.Threading.ThreadPool.QueueUserWorkItem((data) => { try { callback(data); } catch (Exception ex) { //寫(xiě)日志 } }); } catch (Exception e) { //寫(xiě)日志 } return false; } }

這里寫(xiě)圖片描述 其中Execute()方法就是把ThreadPool.QueueUserWorkItem用匿名函數(shù)的方法進(jìn)行了封裝,catch住了異常.

我們?cè)谇懊嬲f(shuō)過(guò)了,線程池中的線程,默認(rèn)是后臺(tái)線程,前面幾個(gè)例子,最后都有句Console.ReadKey(),就是如果不手動(dòng)關(guān)閉主程序,主程序是不會(huì)自動(dòng)關(guān)的. 這肯定不是我們想要的,那我們?cè)趺磁袛嗨凶泳€程運(yùn)行完畢后,關(guān)閉主線程呢 4.5 WaitHandle.WaitAll() 等待所有子線程完成后,關(guān)閉主線程 多個(gè)線程之間的協(xié)調(diào)工作

class Program { static void Main(string[] args) { WaitHandle[] waits = new WaitHandle[2] { new AutoResetEvent(false), new AutoResetEvent(false) }; Person person = new Person() { Name = "Somnus", Age = 10, Wait=waits[0] }; Person person2 = new Person() { Name = "cnblogs", Age = 20, Wait = waits[1] }; ThreadExecutor.Execute(new WaitCallback(ShowMessage),person); ThreadExecutor.Execute(new WaitCallback(ShowMessage), person2); for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } WaitHandle.WaitAll(waits); //WaitHandle.WaitAny(waits); } static void ShowMessage(object state) { Person person = (Person)state; AutoResetEvent are = (AutoResetEvent)person.Wait; Console.WriteLine("學(xué)生姓名是{0},年齡為{1}", person.Name, person.Age); Thread.Sleep(3000); are.Set(); } } class Person { public string Name { get; set; } public int Age { get; set; } public WaitHandle Wait { get; set; } } public class ThreadExecutor { public static bool Execute(System.Threading.WaitCallback callback, object state) { try { return System.Threading.ThreadPool.QueueUserWorkItem((data) => { try { callback(data); } catch (Exception ex) { //寫(xiě)日志 } }, state); } catch (Exception e) { //寫(xiě)日志 } return false; } public static bool Execute(System.Threading.WaitCallback callback) { try { return System.Threading.ThreadPool.QueueUserWorkItem((data) => { try { callback(data); } catch (Exception ex) { //寫(xiě)日志 } }); } catch (Exception e) { //寫(xiě)日志 } return false; } }

這里寫(xiě)圖片描述 WaitHandle.WaitAll(),最大可監(jiān)測(cè)64個(gè)WaitHandler ,如果你需要的多線程比較多,你可以分批,中間Sleep()一段時(shí)間,就可以了. WaitHandle.WaitAny(),其中某一個(gè)線程完成后,就退出主線程.

4.6 委托類(lèi) 使用CLR線程池中的工作者線程,最靈活最常用的方式就是使用委托的異步方法.委托包括下面3個(gè)重要方法:Invoke(),BeginInvoke(),EndInvoke() 當(dāng)調(diào)用Invoke()方法時(shí),對(duì)應(yīng)此委托的所有方法都會(huì)被執(zhí)行。而B(niǎo)eginInvoke與EndInvoke則支持委托方法的異步調(diào)用,由BeginInvoke啟動(dòng)的線程都屬于CLR線程池中的工作者線程。

class Program { static void Main(string[] args) { SleepDelegate sleepDelegate = new SleepDelegate(Sleep); IAsyncResult result = sleepDelegate.BeginInvoke("Sleep", null, null); string data = sleepDelegate.EndInvoke(result); Console.WriteLine(data); Console.ReadKey(); } delegate string SleepDelegate(object o); //模擬執(zhí)行長(zhǎng)時(shí)間的任務(wù) static string Sleep(object state) { string name = (string)state; ThreadMessage(name); Console.WriteLine("我還沒(méi)有執(zhí)行完呢,請(qǐng)耐心等待...."); Thread.Sleep(3000); return "Hello" + name; } static void ThreadMessage(string data) { string message = string.Format("ThreadName is {0} ThreadId is:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); } }

這里寫(xiě)圖片描述 委托還有個(gè)可以調(diào)用回調(diào)函數(shù)的

class Program { static void Main(string[] args) { SleepDelegate sleepDelegate = new SleepDelegate(Sleep); IAsyncResult result = sleepDelegate.BeginInvoke("Sleep", new AsyncCallback(Complete), "這里可以傳個(gè)object值啊"); Console.ReadKey(); } delegate string SleepDelegate(object o); //模擬執(zhí)行長(zhǎng)時(shí)間的任務(wù) static string Sleep(object state) { string name = (string)state; ThreadMessage(name); Console.WriteLine("我還沒(méi)有執(zhí)行完呢,請(qǐng)耐心等待...."); Thread.Sleep(3000); return "Hello" + name; } static void Complete(IAsyncResult iresult) { AsyncResult result = (AsyncResult)iresult; SleepDelegate sleepDelegate = (SleepDelegate)result.AsyncDelegate; string obj = (string)result.AsyncState; string data = sleepDelegate.EndInvoke(result); Console.WriteLine(obj + data); } static void ThreadMessage(string data) { string message = string.Format("ThreadName is {0} ThreadId is:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); } }

這里寫(xiě)圖片描述 ok,就寫(xiě)到這吧,委托異步處理的異常處理和等待所有線程完成,都可以參照4.4和4.5. 同時(shí),在delegate.EndInvoke() 處也可以catch住異常.(這個(gè)在執(zhí)行exe時(shí)候可看到效果,直接調(diào)試程序要報(bào)錯(cuò)) 這篇文章,是自己對(duì)多線程學(xué)習(xí)和總結(jié)吧. 對(duì)于多線程中的,線程安全,不是太了解,可能是下一步要探究的對(duì)象吧.

http://blog.csdn.net/wilsonke/article/details/7616984


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 安溪县| 绥化市| 绥江县| 双辽市| 丰宁| 三河市| 特克斯县| 灵宝市| 大英县| 商南县| 甘肃省| 江油市| 建昌县| 罗江县| 泽普县| 绥中县| 白沙| 泾川县| 清流县| 伊吾县| 罗城| 红安县| 五家渠市| 苏尼特左旗| 延川县| 宁明县| 盘锦市| 乾安县| 新河县| 都安| 山阳县| 金平| 双峰县| 济宁市| 电白县| 邛崃市| 福贡县| 田东县| 淳化县| 施秉县| 施秉县|