亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

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

2019-11-11 03:29:19
字體:
來源:轉載
供稿:網友

先表明,向作者致敬http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html 風塵浪子 前半部分是復制風塵浪子的,從 三 開始,互聯網收集整理. 感謝互聯網,感謝open source. 重要是,大家能夠領悟,掌握和運用多線程的知識.

一、線程的定義

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

1.2 多線程

在單CPU系統的一個單位時間(time slice)內,CPU只能運行單個線程,運行順序取決于線程的優先級別。如果在單位時間內線程未能完成執行,系統就會把線程的狀態信息保存到線程的本地 存儲器(TLS) 中,以便下次執行時恢復執行。而多線程只是系統帶來的一個假像,它在多個單位時間內進行多個線程的切換。因為切換頻密而且單位時間非常短暫,所以多線程可 被視作同時運行。 適當使用多線程能提高系統的性能,比如:在系統請求大容量的數據時使用多線程,把數據輸出工作交給異步線程,使主線程保持其穩定性去處理其他問題。但需要注意一點,因為CPU需要花費不少的時間在線程的切換上,所以過多地使用多線程反而會導致性能的下降。

二、線程的基礎知識

2.1 System.Threading.Thread類 System.Threading.Thread是用于控制線程的基礎類,通過Thread可以控制當前應用程序域中線程的創建、掛起、停止、銷毀。 它包括以下常用公共屬性:

屬性名稱 說明
CurrentContext 獲取線程正在其中執行的當前上下文。
CurrentThread 獲取當前正在運行的線程。
ExecutionContext 獲取一個 ExecutionContext 對象,該對象包含有關當前線程的各種上下文的信息。
IsAlive 獲取一個值,該值指示當前線程的執行狀態。
IsBackground 獲取或設置一個值,該值指示某個線程是否為后臺線程。
IsThreadPoolThread 獲取一個值,該值指示線程是否屬于托管線程池。
ManagedThreadId 獲取當前托管線程的唯一標識符。
Name 獲取或設置線程的名稱。
Priority 獲取或設置一個值,該值指示線程的調度優先級。
ThreadState 獲取一個值,該值包含當前線程的狀態。

2.1.1 線程的標識符 ManagedThreadId是確認線程的唯一標識符,程序在大部分情況下都是通過Thread.ManagedThreadId來辨別線程的。 而Name是一個可變值,在默認時候,Name為一個空值 Null,開發人員可以通過程序設置線程的名稱,但這只是一個輔助功能。

2.1.2 線程的優先級別 .NET為線程設置了Priority屬性來定義線程執行的優先級別,里面包含5個選項,其中Normal是默認值。除非系統有特殊要求,否則不應該隨便設置線程的優先級別。

成員名稱 說明
Lowest 可以將 Thread 安排在具有任何其他優先級的線程之后。
BelowNormal 可以將 Thread 安排在具有 Normal 優先級的線程之后,在具有 Lowest 優先級的線程之前。
Normal 默認選擇??梢詫?Thread 安排在具有 AboveNormal 優先級的線程之后,在具有 BelowNormal 優先級的線程之前。
AboveNormal 可以將 Thread 安排在具有 Highest 優先級的線程之后,在具有 Normal 優先級的線程之前。
Highest 可以將 Thread 安排在具有任何其他優先級的線程之前。

2.1.3 線程的狀態 通過ThreadState可以檢測線程是處于Unstarted、Sleeping、Running 等等狀態,它比 IsAlive 屬性能提供更多的特定信息。 前面說過,一個應用程序域中可能包括多個上下文,而通過CurrentContext可以獲取線程當前的上下文。 CurrentThread是最常用的一個屬性,它是用于獲取當前運行的線程。

2.1.4 System.Threading.Thread的方法 Thread 中包括了多個方法來控制線程的創建、掛起、停止、銷毀,以后來的例子中會經常使用。

方法名稱 說明
Abort() 終止本線程。
GetDomain() 返回當前線程正在其中運行的當前域。
GetDomainId() 返回當前線程正在其中運行的當前域Id。
Interrupt() 中斷處于 WaitSleepJoin 線程狀態的線程。
Join() 已重載。 阻塞調用線程,直到某個線程終止時為止。
Resume() 繼續運行已掛起的線程。
Start() 執行本線程。
Suspend() 掛起當前線程,如果當前線程已屬于掛起狀態則此不起作用
Sleep() 把正在運行的線程掛起一段時間。

2.1.5 開發實例 以下這個例子,就是通過Thread顯示當前線程信息

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(); }

這里寫圖片描述

三、以ThreadStart方式實現多線程

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); } } //模擬執行長時間的任務 static void Sleep() { ThreadMessage("Sleep"); Console.WriteLine("我還沒有執行完呢,請耐心等待...."); 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); }

這里寫圖片描述 運行后你會發現,Sleep()方法 還沒有執行完(睡眠3分鐘模擬長時間任務),主線程已經執行完成. 這就是多線程的好處. 3.2 ParameterizedThreadStart 帶參數的

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); } } //模擬執行長時間的任務 static void Sleep(object state) { string name = (string)state; ThreadMessage(name); Console.WriteLine("我還沒有執行完呢,請耐心等待...."); 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); }

這里寫圖片描述 運行結果和上面那差不多一樣.

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

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); } } //模擬執行長時間的任務 static void Sleep(object state) { string name = (string)state; ThreadMessage(name); Console.WriteLine("我還沒有執行完呢,請耐心等待...."); Thread.Sleep(3000); } //這里的參數不為object,你可以定義任何類型,任何參數 static void ShowMessage(string message) { ThreadMessage(message); Console.WriteLine("我還沒有執行完呢,請耐心等待...."); 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); } }}

這里寫圖片描述 上面的幾種方式都可以用. 我為什么要用個ShowMessage(string msg) 方法來測試呢 ? 細心你會發現ParameterizedThreadStart委托他定義的參數為object,用匿名函數就可以解決這問題.如果你對委托,匿名函數不太熟悉的話,你就要補習一下關于委托的知識了.

3.4 前臺線程and后臺線程 注意以上兩個例子都沒有使用Console.ReadKey(),但系統依然會等待異步線程完成后才會結束。這是因為使用Thread.Start()啟動的線程默認為前臺線程,而系統必須等待所有前臺線程運行結束后,應用程序域才會自動卸載。 在第二節曾經介紹過線程Thread有一個屬性IsBackground,通過把此屬性設置為true,就可以把線程設置為后臺線程!這時應用程序域將在主線程完成時就被卸載(主線程關閉),而不會等待異步線程的運行。

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

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(); } //以異步方式調用 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++) { //當n等于4時,終止線程 if (n >= 4) { Thread.CurrentThread.Abort(n); } Thread.Sleep(300); Console.WriteLine("The number is:" + n.ToString()); } } catch (ThreadAbortException ex) { //輸出終止線程時n的值 if (ex.ExceptionState != null) Console.WriteLine(string.Format("Thread abort when the number is: {0}!", ex.ExceptionState.ToString())); //取消終止,繼續執行線程 Thread.ResetAbort(); Console.WriteLine("Thread ResetAbort!"); } //線程結束 Console.WriteLine("Thread Close!"); } }

這里寫圖片描述

到此,我們學會了運用多線程,可不能沾沾自喜,這可只是剛剛開始. 前面說了通過ThreadStart創建的線程比較難管理,創建過多性能也會下降.主要是因為ThreadStart創建的線程不能循環利用,比如我們For循環個list,每一個model都開啟個線程去執行任務,當前面的線程執行完了,也就銷毀了,后面的還是要重新創建,不停的創建線程是很耗時的.由此可見 .NET為線程管理專門設置了一個CLR線程池.

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

4.1 關于CLR線程池 使用ThreadStart與ParameterizedThreadStart建立新線程非常簡單,但通過此方法建立的線程難于管理,若建立過多的線程反而會影響系統的性能。 有 見及此,.NET引入CLR線程池這個概念。CLR線程池并不會在CLR初始化的時候立刻建立線程,而是在應用程序要創建線程來執行任務時,線程池才初始 化一個線程。線程的初始化與其他的線程一樣。在完成任務以后,該線程不會自行銷毀,而是以掛起的狀態返回到線程池。直到應用程序再次向線程池發出請求時, 線程池里掛起的線程就會再度激活執行任務。這樣既節省了建立線程所造成的性能損耗,也可以讓多個任務反復重用同一線程,從而在應用程序生存期內節約大量開銷. 4.2 工作者線程與I/O線程

CLR線程池分為工作者線程(workerThreads)與I/O線程 (completionPortThreads) 兩種,工作者線程是主要用作管理CLR內部對象的運作,I/O(Input/Output) 線程顧名思義是用于與外部系統交換信息,IO線程的細節將在下一節詳細說明。

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

使用CLR線程池的工作者線程一般有兩種方式,一是直接通過 ThreadPool.QueueUserWorkItem() 方法,二是通過委托(異步操作是加入在線程池中的),下面將逐一細說。 4.3 通過QueueUserWorkItem啟動工作者線程 ThreadPool線程池中包含有兩個靜態方法可以直接啟動工作者線程: 一為 ThreadPool.QueueUserWorkItem(WaitCallback) 二為 ThreadPool.QueueUserWorkItem(WaitCallback,Object) 先把WaitCallback委托指向一個帶有Object參數的無返回值方法,再使用 ThreadPool.QueueUserWorkItem(WaitCallback) 就可以異步啟動此方法,此時異步方法的參數被視為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("我還沒有執行完呢,請耐心等待...."); 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); } }

這里寫圖片描述 這是個不帶參數的, ThreadPool.QueueUserWorkItem()有2個參數,第二個是個object類型

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("學生姓名是{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; } }

這里寫圖片描述 我們創建了一個Person類,在加入線程池的時候,我們傳了一個person , 在ShowMessage()方法中,得到傳過來的person,并顯示人的信息. 那在這里,我們可以效仿3.3匿名函數在線程池中的應用呢? 答案是可以的. 4.4 線程池中的異常處理 多線程執行任務的時候,有時候總是要出錯嘛, 重要的是,我們能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) { //寫日志 } for (int i = 0; i < 10; i++) { Console.WriteLine("這里是主線程在工作" + i); } Console.ReadKey(); } static void ShowMessage(object state) { Person person = (Person)state; throw new Exception("這里出錯了,快catch住我"); Console.WriteLine("學生姓名是{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; } }

這里寫圖片描述 這樣? No~ 結果會差強人意的,我們并沒有catch住他. 那該在哪try呢. 對, 在委托調用那方法中. 我們這樣干,

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("這里出錯了,快catch住我"); Console.WriteLine("學生姓名是{0},年齡為{1}", person.Name, person.Age); Thread.Sleep(3000); } catch (Exception ex) { //寫日志 } } } class Person { public string Name { get; set; } public int Age { get; set; } //public WaitHandle Wait { get; set; } }

這里寫圖片描述 這樣,程序就不會報錯了. 如果有好幾個任務方法, 那我們要寫好幾個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("這里出錯了,快catch住我"); Console.WriteLine("學生姓名是{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) { //寫日志 } }, state); } catch (Exception e) { //寫日志 } return false; } public static bool Execute(System.Threading.WaitCallback callback) { try { return System.Threading.ThreadPool.QueueUserWorkItem((data) => { try { callback(data); } catch (Exception ex) { //寫日志 } }); } catch (Exception e) { //寫日志 } return false; } }

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

我們在前面說過了,線程池中的線程,默認是后臺線程,前面幾個例子,最后都有句Console.ReadKey(),就是如果不手動關閉主程序,主程序是不會自動關的. 這肯定不是我們想要的,那我們怎么判斷所有子線程運行完畢后,關閉主線程呢 4.5 WaitHandle.WaitAll() 等待所有子線程完成后,關閉主線程 多個線程之間的協調工作

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("學生姓名是{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) { //寫日志 } }, state); } catch (Exception e) { //寫日志 } return false; } public static bool Execute(System.Threading.WaitCallback callback) { try { return System.Threading.ThreadPool.QueueUserWorkItem((data) => { try { callback(data); } catch (Exception ex) { //寫日志 } }); } catch (Exception e) { //寫日志 } return false; } }

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

4.6 委托類 使用CLR線程池中的工作者線程,最靈活最常用的方式就是使用委托的異步方法.委托包括下面3個重要方法:Invoke(),BeginInvoke(),EndInvoke() 當調用Invoke()方法時,對應此委托的所有方法都會被執行。而BeginInvoke與EndInvoke則支持委托方法的異步調用,由BeginInvoke啟動的線程都屬于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); //模擬執行長時間的任務 static string Sleep(object state) { string name = (string)state; ThreadMessage(name); Console.WriteLine("我還沒有執行完呢,請耐心等待...."); 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); } }

這里寫圖片描述 委托還有個可以調用回調函數的

class Program { static void Main(string[] args) { SleepDelegate sleepDelegate = new SleepDelegate(Sleep); IAsyncResult result = sleepDelegate.BeginInvoke("Sleep", new AsyncCallback(Complete), "這里可以傳個object值啊"); Console.ReadKey(); } delegate string SleepDelegate(object o); //模擬執行長時間的任務 static string Sleep(object state) { string name = (string)state; ThreadMessage(name); Console.WriteLine("我還沒有執行完呢,請耐心等待...."); 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); } }

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

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


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕欧美专区| 91麻豆国产语对白在线观看| 亚洲欧美成人一区二区在线电影| 欧美午夜女人视频在线| 欧美大片欧美激情性色a∨久久| 国内精品久久久久影院优| 国产精品高清免费在线观看| 91国产视频在线| 久久久久久国产| 欧美一性一乱一交一视频| 欧美日韩国产丝袜另类| 97成人精品区在线播放| 亚洲深夜福利网站| 国产91亚洲精品| 社区色欧美激情 | 欧美激情区在线播放| 国产香蕉精品视频一区二区三区| 亚洲国产精品国自产拍av秋霞| 97视频在线观看视频免费视频| 92看片淫黄大片看国产片| 国产精品美女免费看| 亚洲综合日韩中文字幕v在线| 亚洲区一区二区| 国产精品揄拍一区二区| 欧美猛男性生活免费| 午夜精品久久久久久久久久久久| 日韩69视频在线观看| 国产亚洲精品久久久久久777| 国产精品免费久久久久影院| 国产日韩精品综合网站| 国产69精品久久久久9| 久久69精品久久久久久久电影好| 国产日韩欧美中文在线播放| 亚洲高清色综合| 国产精品免费观看在线| 国产日韩欧美影视| 精品亚洲va在线va天堂资源站| 日韩专区在线观看| 国产精品av免费在线观看| 国产99久久精品一区二区永久免费| 一级做a爰片久久毛片美女图片| 欧美成人在线网站| 欧洲美女7788成人免费视频| 91欧美精品午夜性色福利在线| 亚洲美女久久久| 中日韩午夜理伦电影免费| 久久久久久亚洲精品中文字幕| 亚洲第一av在线| 亚洲级视频在线观看免费1级| 在线观看国产精品91| 久久精品一本久久99精品| 乱亲女秽乱长久久久| 亚洲欧美在线一区二区| 91精品综合久久久久久五月天| 亚洲自拍偷拍福利| 欧美亚洲免费电影| 久久伊人精品视频| 久久亚洲国产精品成人av秋霞| 欧美大成色www永久网站婷| 国产精品av在线| 色综合天天狠天天透天天伊人| 中国china体内裑精亚洲片| 亚洲精品福利在线| 亚洲欧美日韩国产精品| xvideos亚洲人网站| 在线播放亚洲激情| 欧美黄色三级网站| 成人免费福利在线| 欧美性受xxx| 欧美激情久久久| 91影院在线免费观看视频| 亚洲电影av在线| 精品性高朝久久久久久久| 欧美理论电影在线播放| 精品福利在线视频| 日韩久久精品电影| 日产精品99久久久久久| 欧美一区二区三区免费观看| 亚洲国产又黄又爽女人高潮的| 精品国产一区久久久| 国产精品日韩精品| 精品日韩美女的视频高清| 欧美国产视频一区二区| 91在线无精精品一区二区| 欧美国产亚洲精品久久久8v| 亚洲一区二区三区在线免费观看| 九九精品视频在线观看| 成人一区二区电影| 91精品91久久久久久| 日韩av手机在线| 欧美激情亚洲国产| 欧美精品制服第一页| 91在线观看免费观看| 成人精品在线观看| 日韩中文字幕在线视频| 国产区精品视频| 成人网址在线观看| 亚洲精品suv精品一区二区| 91精品国产色综合久久不卡98口| 欧美日韩国产中文精品字幕自在自线| 欧美日韩国产成人在线观看| 午夜精品久久久久久久99热浪潮| 国产精品直播网红| 久久久91精品国产一区不卡| 91九色视频导航| wwwwwwww亚洲| 国产精品视频公开费视频| 欧美精品在线极品| 日韩av在线直播| 美女黄色丝袜一区| 欧美成人午夜影院| 久久91超碰青草是什么| 国自在线精品视频| 成人精品网站在线观看| 欧美日韩性生活视频| 青青草原一区二区| 国外日韩电影在线观看| 久久精品电影网站| 久久久av电影| 成人妇女免费播放久久久| 色视频www在线播放国产成人| 91成人免费观看网站| 国产日本欧美一区| 国产精品伦子伦免费视频| 亚洲一区二区三区香蕉| 国产精品成人在线| 欧美人在线观看| 精品福利樱桃av导航| 久久久久久九九九| 亚洲精品v欧美精品v日韩精品| 日韩av网站电影| 亚洲美女喷白浆| 国产中文字幕日韩| 精品色蜜蜜精品视频在线观看| 国产精品久久久亚洲| 国产精品久久久久久久久免费| 久久视频国产精品免费视频在线| 欧美日韩国产综合视频在线观看中文| 欧美日韩亚洲视频一区| 高清欧美性猛交xxxx黑人猛交| 国产丝袜一区二区三区免费视频| 久久久av亚洲男天堂| 97精品在线观看| 亚洲a一级视频| 成人疯狂猛交xxx| 国产精品夫妻激情| 亚洲sss综合天堂久久| 久久91精品国产91久久久| 精品国产欧美成人夜夜嗨| 日韩精品在线观| 国产精品自产拍高潮在线观看| 清纯唯美日韩制服另类| 欧美做受高潮1| 97精品国产97久久久久久春色| 国产精品自在线| 一区二区在线免费视频| 在线播放精品一区二区三区| 91视频国产一区| www.美女亚洲精品| 色综合伊人色综合网站| 97超级碰碰碰久久久| 91高清视频免费观看| 国产精品主播视频| 亚洲一区二区免费在线|