一.CLR線程池並不會在CLR初始化時立即建立線程,而是在應用程序要創建線程來運行任務時,線程池才初始化一個線程。
//通過CLR線程池所建立的線程總是默認為後台線程,優先級數為ThreadPriority.Normal。
1.線程池初始化時是沒有線程的,線程池裏的線程的初始化與其他線程一樣,但是在完成任務以後,該線程不會自行銷燬,而是以掛起的狀態返回到線程池。
2.直到應用程序再次向線程池發出請求時,線程池裏掛起的線程就會再度激活執行任務。
3.這樣既節省了建立線程所造成的性能損耗,也可以讓多個任務反覆重用同一線程,從而在應用程序生存期內節約大量開銷。
二.CLR線程池分為工作者線程(workerThreads)與I/O線程(completionPortThreads)兩種:
1.工作者線程是主要用作管理CLR內部對象的運作,通常用於計算密集的任務。
2.I/O(Input/Output)線程主要用於與外部系統交互信息,如輸入輸出,CPU僅需在任務開始的時候,將任務的參數傳遞給設備,然後啓動硬件設備即可。
三.使用CLR線程池的工作者線程一般有兩種方式:
1.通過ThreadPool.QueueUserWorkItem()方法;
(1)ThreadPool線程池中有兩個重載的靜態方法可以直接啓動工作者線程:
ThreadPool.QueueUserWorkItem(WaitCallback(方法名));
//啓動工作者線程(無參數)
ThreadPool.QueueUserWorkItem(new WaitCallback(RunMyMethod));
ThreadPool.QueueUserWorkItem(new WaitCallback(方法名),Object對象參數);
使用第二個重載方法ThreadPool.QueueUserWorkItem(WaitCallback,object)方法可以把object對象作為參數傳送到回調函數中。
//啓動工作者線程(有參數 obj類型)
ThreadPool.QueueUserWorkItem(new WaitCallback(MyMethod), Obj);
void MyMethod(object Obj){//..}
WaitCallback委託指向的必須是一個帶有object參數的無返回值方法,傳遞多個參數只有通過委託
2.通過委託;
使用BeginInvoke與EndInvoke委託異步調用線程
建立一個委託對象,通過IAsyncResult BeginInvoke(string name,AsyncCallback callback,object state)異步調用委託方法,BeginInvoke方法除最後的兩個參數外,其他參數都是與方法參數相對應的。
利用EndInvoke(IAsyncResult--上一步BeginInvoke返回的對象)方法就可以結束異步操作,獲取委託的運行結果。
//聲明委託
//delegate string MyDelegate(string Name,int Age);
//建立委託
MyDelegate myDelegate = new MyDelegate(GetInfo);
//異步調用委託,除最後兩個參數外,前面的參數都可以傳進去
IAsyncResult result = myDelegate.BeginInvoke("小明",18, null, null); //IAsynResult還能輪詢判斷
//調用EndInvoke(IAsyncResult)獲取運行結果
string data = myDelegate.EndInvoke(result);
//調用的方法
private string GetInfo(string Name,int Age)
{
return string.Format("我是{0},今年{1}歲!",name,age);
}
要注意的是並不知道異步操作什麼時候執行完,什麼時候開始調用EndInvoke,因為一旦EndInvoke主線程就會處於阻塞等待狀態。這時需要用到IAsyncResult提高主線程的工作性能
//IAsyncResult屬性
public interface IAsyncResult
{
object AsyncState {get;} //獲取用户定義的對象,它限定或包含關於異步操作的信息。
WailHandle AsyncWaitHandle {get;} //獲取用於等待異步操作完成的 WaitHandle。
bool CompletedSynchronously {get;} //獲取異步操作是否同步完成的指示。
bool IsCompleted {get;} //獲取異步操作是否已完成的指示。
}
//利用IsCompleted屬性,來判斷異步線程是否完成
while (!result.IsCompleted)
{
Thread.Sleep(500);
Console.WriteLine("異步線程還沒完成,主線程幹其他事!");
}
//除了IsCompleted屬性外,還可以使用AsyncWaitHandle如下3個方法實現同樣輪詢判斷效果
//WaitOne:判斷單個異步線程是否完成;
//WaitAny:判斷是否異步線程是否有指定數量個已完成;
//WaitAll:判斷是否所有的異步線程已完成;
//WaitOne:
//比上個例子,判斷條件由IsCompleted屬性換成了AsyncWaitHandle,僅此而已
while (!result.AsyncWaitHandle.WaitOne(200))
{
Console.WriteLine("異步線程沒完,主線程繼續幹活!");
}
//WaitAny:
//是否完成了指定數量
WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle };
while (WaitHandle.WaitAny(waitHandleList, 200) > 0)
{
Console.WriteLine("異步線程完成數未大於0,主線程繼續甘其他事!");
}
//WaitAll:
WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle };
//是否全部異步線程完成
while (!WaitHandle.WaitAll(waitHandleList, 200))
{
Console.WriteLine("異步線程未全部完成,主線程繼續幹其他事!");
}
//IAsyncResult回調函數
//倒數第二個參數,委託中綁定了完成後的回調方法
IAsyncResult result1 = myDelegate.BeginInvoke("小明",23, new AsyncCallback(Completed), null);
//供異步線程完成回調的方法
static void Completed(IAsyncResult result)
{
//獲取委託對象,調用EndInvoke方法獲取運行結果
AsyncResult _result = (AsyncResult)result;
MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate;
//獲得參數
string data = myDelegaate.EndInvoke(_result);
Console.WriteLine(data);
//異步線程執行完畢
Console.WriteLine("異步線程完成咯!");
Console.WriteLine("回調函數也是由" + Thread.CurrentThread.Name + "調用的!");
}
}
//回調函數依然是在輔助線程中執行的,這樣就不會影響主線程的運行。
//線程池的線程默認是後台線程。但是如果主線程比輔助線程優先完成,那麼程序已經卸載,回調函數未必會執行。如果不希望丟失回調函數中的操作,要麼把異步線程設為前台線程,要麼確保主線程將比輔助線程遲完成。
//BeginInvoke("劉備",23, new AsyncCallback(Completed), null)最後一個參數用來傳參
Person p = new Person(2,"關羽");
IAsyncResult result1 = myDelegate.BeginInvoke("劉備", 23, new AsyncCallback(Completed), p);
//供異步線程完成回調的方法
static void Completed(IAsyncResult result)
{
//獲取委託對象,調用EndInvoke方法獲取運行結果
AsyncResult _result = (AsyncResult)result;
MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate;
//獲得參數
string data = myDelegaate.EndInvoke(_result);
Console.WriteLine(data);
Person p = result.AsyncState as Person;
Console.WriteLine("傳過來的參數是:" + p.Name);
//異步線程執行完畢
Console.WriteLine("異步線程完成咯!");
Console.WriteLine("回調函數也是由" + Thread.CurrentThread.Name + "調用的!");
}