目錄
一、線程的定義
二、線程的基礎(chǔ)知識
三、以ThreadStart方式實現(xiàn)多線程
四、CLR線程池的工作者線程
五、CLR線程池的I/O線程
六、異步 SqlCommand
七、并行編程與PLINQ
八、計時器與鎖
四、CLR線程池的工作者線程
4.1 關(guān)于CLR線程池
使用ThreadStart與ParameterizedThreadStart建立新線程非常簡單,但通過此方法建立的線程難于管理,若建立過多的線程反而會影響系統(tǒng)的性能。
有 見及此,.NET引入CLR線程池這個概念。CLR線程池并不會在CLR初始化的時候立刻建立線程,而是在應(yīng)用程序要創(chuàng)建線程來執(zhí)行任務(wù)時,線程池才初始 化一個線程。線程的初始化與其他的線程一樣。在完成任務(wù)以后,該線程不會自行銷毀,而是以掛起的狀態(tài)返回到線程池。直到應(yīng)用程序再次向線程池發(fā)出請求時, 線程池里掛起的線程就會再度激活執(zhí)行任務(wù)。這樣既節(jié)省了建立線程所造成的性能損耗,也可以讓多個任務(wù)反復(fù)重用同一線程,從而在應(yīng)用程序生存期內(nèi)節(jié)約大量開 銷。
注意:通過CLR線程池所建立的線程總是默認(rèn)為后臺線程,優(yōu)先級數(shù)為ThreadPriority.Normal。
4.2 工作者線程與I/O線程
CLR線程池分為工作者線程(workerThreads)與I/O線程 (completionPortThreads) 兩種,工作者線程是主要用作管理CLR內(nèi)部對象的運作,I/O(Input/Output) 線程顧名思義是用于與外部系統(tǒng)交換信息,IO線程的細(xì)節(jié)將在下一節(jié)詳細(xì)說明。
通 過ThreadPool.GetMax(out int workerThreads,out int completionPortThreads )和 ThreadPool.SetMax( int workerThreads, int completionPortThreads)兩個方法可以分別讀取和設(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。
若想測試線程池中有多少的線程正在投入使用,可以通過ThreadPool.GetAvailableThreads( out int workerThreads,out int completionPortThreads ) 方法。
使用CLR線程池的工作者線程一般有兩種方式,一是直接通過 ThreadPool.QueueUserWorkItem() 方法,二是通過委托,下面將逐一細(xì)說。
4.3 通過QueueUserWorkItem啟動工作者線程
ThreadPool線程池中包含有兩個靜態(tài)方法可以直接啟動工作者線程:
一為 ThreadPool.QueueUserWorkItem(WaitCallback)
二為 ThreadPool.QueueUserWorkItem(WaitCallback,Object)
先把WaitCallback委托指向一個帶有Object參數(shù)的無返回值方法,再使用 ThreadPool.QueueUserWorkItem(WaitCallback) 就可以異步啟動此方法,此時異步方法的參數(shù)被視為null 。
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //把CLR線程池的最大值設(shè)置為1000 6 ThreadPool.SetMaxThreads(1000, 1000);
7 //顯示主線程啟動時線程池信息 8 ThreadMessage("Start");
9 //啟動工作者線程 10 ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncCallback));
11 Console.ReadKey();
12 }
13
14 static void AsyncCallback(object state)
15 {
16 Thread.Sleep(200);
17 ThreadMessage("AsyncCallback");
18 Console.WriteLine("Async thread do work!");
19 }
20
21 //顯示線程現(xiàn)狀 22 static void ThreadMessage(string data)
23 {
24 string message = string.Format("{0}\n CurrentThreadId is {1}",
25 data, Thread.CurrentThread.ManagedThreadId);
26 Console.WriteLine(message);
27 }
28 }
運行結(jié)果
使用 ThreadPool.QueueUserWorkItem(WaitCallback,Object) 方法可以把object對象作為參數(shù)傳送到回調(diào)函數(shù)中。
下面例子中就是把一個string對象作為參數(shù)發(fā)送到回調(diào)函數(shù)當(dāng)中。
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //把線程池的最大值設(shè)置為1000 6 ThreadPool.SetMaxThreads(1000, 1000);
7
8 ThreadMessage("Start");
9 ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncCallback),"Hello Elva");
10 Console.ReadKey();
11 }
12
13 static void AsyncCallback(object state)
14 {
15 Thread.Sleep(200);
16 ThreadMessage("AsyncCallback");
17
18 string data = (string)state;
19 Console.WriteLine("Async thread do work!\n"+data);
20 }
21
22 //顯示線程現(xiàn)狀 23 static void ThreadMessage(string data)
24 {
25 string message = string.Format("{0}\n CurrentThreadId is {1}",
26 data, Thread.CurrentThread.ManagedThreadId);
27 Console.WriteLine(message);
28 }
29 }
運行結(jié)果
通 過ThreadPool.QueueUserWorkItem啟動工作者線程雖然是方便,但WaitCallback委托指向的必須是一個帶有 Object參數(shù)的無返回值方法,這無疑是一種限制。若方法需要有返回值,或者帶有多個參數(shù),這將多費周折。有見及此,.NET提供了另一種方式去建立工 作者線程,那就是委托。
4.4 委托類
使用CLR線程池中的工作者線程,最靈活最常用的方式就是使用委托的異步方法,在此先簡單介紹一下委托類。
當(dāng)定義委托后,.NET就會自動創(chuàng)建一個代表該委托的類,下面可以用反射方式顯示委托類的方法成員(對反射有興趣的朋友可以先參考一下“.NET基礎(chǔ)篇——反射的奧妙”)
1 class Program
2 {
3 delegate void MyDelegate();
4
5 static void Main(string[] args)
6 {
7 MyDelegate delegate1 = new MyDelegate(AsyncThread);
8 //顯示委托類的幾個方法成員 9 var methods=delegate1.GetType().GetMethods();
10 if (methods != null)
11 foreach (MethodInfo info in methods)
12 Console.WriteLine(info.Name);
13 Console.ReadKey();
14 }
15 }
委托類包括以下幾個重要方法
1 public class MyDelegate:MulticastDelegate
2 {
3 public MyDelegate(object target, int methodPtr);
4 //調(diào)用委托方法 5 public virtual void Invoke();
6 //異步委托 7 public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state);
8 public virtual void EndInvoke(IAsyncResult result);
9 }
當(dāng)調(diào)用Invoke()方法時,對應(yīng)此委托的所有方法都會被執(zhí)行。而BeginInvoke與EndInvoke則支持委托方法的異步調(diào)用,由BeginInvoke啟動的線程都屬于CLR線程池中的工作者線程,在下面將詳細(xì)說明。
4.5 利用BeginInvoke與EndInvoke完成異步委托方法
首 先建立一個委托對象,通過IAsyncResult BeginInvoke(string name,AsyncCallback callback,object state) 異步調(diào)用委托方法,BeginInvoke 方法除最后的兩個參數(shù)外,其它參數(shù)都是與方法參數(shù)相對應(yīng)的。通過 BeginInvoke 方法將返回一個實現(xiàn)了 System.IAsyncResult 接口的對象,之后就可以利用EndInvoke(IAsyncResult ) 方法就可以結(jié)束異步操作,獲取委托的運行結(jié)果。
1 class Program
2 {
3 delegate string MyDelegate(string name);
4
5 static void Main(string[] args)
6 {
7 ThreadMessage("Main Thread");
8
9 //建立委托 10 MyDelegate myDelegate = new MyDelegate(Hello);
11 //異步調(diào)用委托,獲取計算結(jié)果 12 IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null);
13 //完成主線程其他工作 14 ............. 15 //等待異步方法完成,調(diào)用EndInvoke(IAsyncResult)獲取運行結(jié)果 16 string data=myDelegate.EndInvoke(result);
17 Console.WriteLine(data);
18
19 Console.ReadKey();
20 }
21
22 static string Hello(string name)
23 {
24 ThreadMessage("Async Thread");
25 Thread.Sleep(2000); //虛擬異步工作
26 return "Hello " + name;
27 }
28
29 //顯示當(dāng)前線程 30 static void ThreadMessage(string data)
31 {
32 string message = string.Format("{0}\n ThreadId is:{1}",
33 data,Thread.CurrentThread.ManagedThreadId);
34 Console.WriteLine(message);
35 }
36 }
運行結(jié)果
4.6 善用IAsyncResult
在 以上例子中可以看見,如果在使用myDelegate.BeginInvoke后立即調(diào)用myDelegate.EndInvoke,那在異步線程未完成 工作以前主線程將處于阻塞狀態(tài),等到異步線程結(jié)束獲取計算結(jié)果后,主線程才能繼續(xù)工作,這明顯無法展示出多線程的優(yōu)勢。此時可以好好利用 IAsyncResult 提高主線程的工作性能,IAsyncResult有以下成員:
1 public interface IAsyncResult
2 {
3 object AsyncState {get;} //獲取用戶定義的對象,它限定或包含關(guān)于異步操作的信息。
4 WailHandle AsyncWaitHandle {get;} //獲取用于等待異步操作完成的 WaitHandle。
5 bool CompletedSynchronously {get;} //獲取異步操作是否同步完成的指示。
6 bool IsCompleted {get;} //獲取異步操作是否已完成的指示。
7 }
通過輪詢方式,使用IsCompleted屬性判斷異步操作是否完成,這樣在異步操作未完成前就可以讓主線程執(zhí)行另外的工作。
1 class Program
2 {
3 delegate string MyDelegate(string name);
4
5 static void Main(string[] args)
6 {
7 ThreadMessage("Main Thread");
8
9 //建立委托 10 MyDelegate myDelegate = new MyDelegate(Hello);
11 //異步調(diào)用委托,獲取計算結(jié)果 12 IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null);
13 //在異步線程未完成前執(zhí)行其他工作 14 while (!result.IsCompleted)
15 {
16 Thread.Sleep(200); //虛擬操作
17 Console.WriteLine("Main thead do work!");
18 }
19 string data=myDelegate.EndInvoke(result);
20 Console.WriteLine(data);
21
22 Console.ReadKey();
23 }
24
25 static string Hello(string name)
26 {
27 ThreadMessage("Async Thread");
28 Thread.Sleep(2000);
29 return "Hello " + name;
30 }
31
32 static void ThreadMessage(string data)
33 {
34 string message = string.Format("{0}\n ThreadId is:{1}",
35 data,Thread.CurrentThread.ManagedThreadId);
36 Console.WriteLine(message);
37 }
38 }
運行結(jié)果:
除 此以外,也可以使用WailHandle完成同樣的工作,WaitHandle里面包含有一個方法WaitOne(int timeout),它可以判斷委托是否完成工作,在工作未完成前主線程可以繼續(xù)其他工作。運行下面代碼可得到與使用 IAsyncResult.IsCompleted 同樣的結(jié)果,而且更簡單方便 。
1 namespace Test
2 {
3 class Program
4 {
5 delegate string MyDelegate(string name);
6
7 static void Main(string[] args)
8 {
9 ThreadMessage("Main Thread");
10
11 //建立委托 12 MyDelegate myDelegate = new MyDelegate(Hello);
13
14 //異步調(diào)用委托,獲取計算結(jié)果 15 IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null);
16
17 while (!result.AsyncWaitHandle.WaitOne(200))
18 {
19 Console.WriteLine("Main thead do work!");
20 }
21 string data=myDelegate.EndInvoke(result);
22 Console.WriteLine(data);
23
24 Console.ReadKey();
25 }
26
27 static string Hello(string name)
28 {
29 ThreadMessage("Async Thread");
30 Thread.Sleep(2000);
31 return "Hello " + name;
32 }
33
34 static void ThreadMessage(string data)
35 {
36 string message = string.Format("{0}\n ThreadId is:{1}",
37 data,Thread.CurrentThread.ManagedThreadId);
38 Console.WriteLine(message);
39 }
40 }
當(dāng)要監(jiān)視多個運行對象的時候,使用IAsyncResult.WaitHandle.WaitOne可就派不上用場了。
幸好.NET為WaitHandle準(zhǔn)備了另外兩個靜態(tài)方法:WaitAny(waitHandle[], int)與WaitAll (waitHandle[] , int)。
其中WaitAll在等待所有waitHandle完成后再返回一個bool值。
而WaitAny是等待其中一個waitHandle完成后就返回一個int,這個int是代表已完成waitHandle在waitHandle[]中的數(shù)組索引。
下面就是使用WaitAll的例子,運行結(jié)果與使用 IAsyncResult.IsCompleted 相同。
1 class Program
2 {
3 delegate string MyDelegate(string name);
4
5 static void Main(string[] args)
6 {
7 ThreadMessage("Main Thread");
8
9 //建立委托 10 MyDelegate myDelegate = new MyDelegate(Hello);
11
12 //異步調(diào)用委托,獲取計算結(jié)果 13 IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null);
14
15 //此處可加入多個檢測對象 16 WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle,........ };
17 while (!WaitHandle.WaitAll(waitHandleList,200))
18 {
19 Console.WriteLine("Main thead do work!");
20 }
21 string data=myDelegate.EndInvoke(result);
22 Console.WriteLine(data);
23
24 Console.ReadKey();
25 }
26
27 static string Hello(string name)
28 {
29 ThreadMessage("Async Thread");
30 Thread.Sleep(2000);
31 return "Hello " + name;
32 }
33
34 static void ThreadMessage(string data)
35 {
36 string message = string.Format("{0}\n ThreadId is:{1}",
37 data,Thread.CurrentThread.ManagedThreadId);
38 Console.WriteLine(message);
39 }
40 }
4.7 回調(diào)函數(shù)
使 用輪詢方式來檢測異步方法的狀態(tài)非常麻煩,而且效率不高,有見及此,.NET為 IAsyncResult BeginInvoke(AsyncCallback , object)準(zhǔn)備了一個回調(diào)函數(shù)。使用 AsyncCallback 就可以綁定一個方法作為回調(diào)函數(shù),回調(diào)函數(shù)必須是帶參數(shù) IAsyncResult 且無返回值的方法: void AsycnCallbackMethod(IAsyncResult result) 。在BeginInvoke方法完成后,系統(tǒng)就會調(diào)用AsyncCallback所綁定的回調(diào)函數(shù),最后回調(diào)函數(shù)中調(diào)用 XXX EndInvoke(IAsyncResult result) 就可以結(jié)束異步方法,它的返回值類型與委托的返回值一致。
1 class Program
2 {
3 delegate string MyDelegate(string name);
4
5 static void Main(string[] args)
6 {
7 ThreadMessage("Main Thread");
8
9 //建立委托 10 MyDelegate myDelegate = new MyDelegate(Hello);
11 //異步調(diào)用委托,獲取計算結(jié)果 12 myDelegate.BeginInvoke("Leslie", new AsyncCallback(Completed), null);
13 //在啟動異步線程后,主線程可以繼續(xù)工作而不需要等待 14 for (int n = 0; n < 6; n++)
15 Console.WriteLine(" Main thread do work!");
16 Console.WriteLine("");
17
18 Console.ReadKey();
19 }
20
21 static string Hello(string name)
22 {
23 ThreadMessage("Async Thread");
24 Thread.Sleep(2000); \\模擬異步操作
25 return "\nHello " + name;
26 }
27
28 static void Completed(IAsyncResult result)
29 {
30 ThreadMessage("Async Completed");
31
32 //獲取委托對象,調(diào)用EndInvoke方法獲取運行結(jié)果 33 AsyncResult _result = (AsyncResult)result;
34 MyDelegate myDelegate = (MyDelegate)_result.AsyncDelegate;
35 string data = myDelegate.EndInvoke(_result);
36 Console.WriteLine(data);
37 }
38
39 static void ThreadMessage(string data)
40 {
41 string message = string.Format("{0}\n ThreadId is:{1}",
42 data, Thread.CurrentThread.ManagedThreadId);
43 Console.WriteLine(message);
44 }
45 }
可以看到,主線在調(diào)用BeginInvoke方法可以繼續(xù)執(zhí)行其他命令,而無需再等待了,這無疑比使用輪詢方式判斷異步方法是否完成更有優(yōu)勢。
在異步方法執(zhí)行完成后將會調(diào)用AsyncCallback所綁定的回調(diào)函數(shù),注意一點,回調(diào)函數(shù)依然是在異步線程中執(zhí)行,這樣就不會影響主線程的運行,這也使用回調(diào)函數(shù)最值得青昧的地方。
在回調(diào)函數(shù)中有一個既定的參數(shù)IAsyncResult,把IAsyncResult強制轉(zhuǎn)換為AsyncResult后,就可以通過 AsyncResult.AsyncDelegate 獲取原委托,再使用EndInvoke方法獲取計算結(jié)果。
運行結(jié)果如下:
如 果想為回調(diào)函數(shù)傳送一些外部信息,就可以利用BeginInvoke(AsyncCallback,object)的最后一個參數(shù)object,它允許外 部向回調(diào)函數(shù)輸入任何類型的參數(shù)。只需要在回調(diào)函數(shù)中利用 AsyncResult.AsyncState 就可以獲取object對象。
1 class Program
2 {
3 public class Person
4 {
5 public string Name;
6 public int Age;
7 }
8
9 delegate string MyDelegate(string name);
10
11 static void Main(string[] args)
12 {
13 ThreadMessage("Main Thread");
14
15 //建立委托 16 MyDelegate myDelegate = new MyDelegate(Hello);
17
18 //建立Person對象 19 Person person = new Person();
20 person.Name = "Elva";
21 person.Age = 27;
22
23 //異步調(diào)用委托,輸入?yún)?shù)對象person, 獲取計算結(jié)果 24 myDelegate.BeginInvoke("Leslie", new AsyncCallback(Completed), person);
25
26 //在啟動異步線程后,主線程可以繼續(xù)工作而不需要等待 27 for (int n = 0; n < 6; n++)
28 Console.WriteLine(" Main thread do work!");
29 Console.WriteLine("");
30
31 Console.ReadKey();
32 }
33
34 static string Hello(string name)
35 {
36 ThreadMessage("Async Thread");
37 Thread.Sleep(2000);
38 return "\nHello " + name;
39 }
40
41 static void Completed(IAsyncResult result)
42 {
43 ThreadMessage("Async Completed");
44
45 //獲取委托對象,調(diào)用EndInvoke方法獲取運行結(jié)果 46 AsyncResult _result = (AsyncResult)result;
47 MyDelegate myDelegate = (MyDelegate)_result.AsyncDelegate;
48 string data = myDelegate.EndInvoke(_result);
49 //獲取Person對象 50 Person person = (Person)result.AsyncState;
51 string message = person.Name + "'s age is " + person.Age.ToString();
52
53 Console.WriteLine(data+"\n"+message);
54 }
55
56 static void ThreadMessage(string data)
57 {
58 string message = string.Format("{0}\n ThreadId is:{1}",
59 data, Thread.CurrentThread.ManagedThreadId);
60 Console.WriteLine(message);
61 }
62 }
億恩-天使(QQ:530997) 電話 037160135991 服務(wù)器租用,托管歡迎咨詢。
本文出自:億恩科技【mszdt.com】
服務(wù)器租用/服務(wù)器托管中國五強!虛擬主機域名注冊頂級提供商!15年品質(zhì)保障!--億恩科技[ENKJ.COM]
|