CLR為每個進程維護了一個線程池,初始時它是空的 。但當一個線程被建立且被進程使用之後,並且完成了它的執行時 ,它並不被銷毀,而是加入到進程的線程池中。之後,當進程再次需要線程時,它會重新利用池中的線程,這樣節省了大量的時間。
線程的複雜性:
儘管多線程的概念很簡單,但使所有的細節都正確是比較困難的,以下需要被考慮:
1〉線程間的通訊, 線程間的通訊僅有很少的內建機制,所以使用記憶體是最簡單的機制,因為記憶體對所有同一進程內的所有線程可見並可被訪問。
2〉線程的協調,線程的建立很簡單,但仍需協調它們的行為。比如一個線程需要等待一個或更多的其他線程完成執行後才可繼續自身的執行。
3〉同步資源的使用, 因為同一進程內的所有線程共用進程的記憶體和資源,必須保證不同的線程不可以同時訪問和改變它們,避免資源/記憶體狀態不一致。
當建立複雜的多線程系統時可以使用System.Threading命名空間下的類和類型,包括Thread類本身和其他的如: Mutex(互斥鎖),Semaphore(訊號量),Monitor(監視器).它們用於資源的同步使用.這些概念都是比較底層且難懂的,最好找些作業系統原理方面的
資料參考.
你可往你的程式中加入強大的多線程功能通過兩個簡單的技術: 非同步委託和定時器(asynchronous delegates and timers),對於大部分程式這就是你可能唯一需要的線程技術。
C# 有個非常易用的使方法非同步執行的機制:delegates(委託)。委派物件都有一個調用列表,當列表中 僅有一個方法時,它可被非同步執行;委託類的BeginInvoke和EndInvoke方法即為此目的。使用步驟如下:
1> 當調用委派物件的BeginInvoke() 方法時,它開始在一個來自線程池的線程中執行委託指向的那個方法,之後立即返回到初始的線程,起初的那個線程將繼續執行下面的代碼.同時被委託執行的那個線程也在並存執行著.
2> 當你的程式想提取非同步執行的方法的結果時,可先檢查由BeginInvoke()方法返回的IAsyncResult的IsCompleted屬性,或調用EndInvoke()方法等待委託執行完成.
三種標準的使用模式:
1>wait-until-done等待直到完成模式,在產生一個非同步方法呼叫的執行後(Beginnvoke()),初始線程做一些其他的處理,之後會掛起並等待非同步方法呼叫的結束,以便繼續執行.不同於同步方法的是同步方法一旦被調用便初始線程便不能做任何其他的處理---控制完全轉移到被調用的方法那裡去了.
2>Polling輪詢模式,初始線程周期性的檢查新生的線程是否完成了執行,沒有的話繼續其他的處理.
3>callback回調模式,初始線程不會等待或檢查卵生線程是否完成了執行,而是:當被委託引用的方法在卵生(spawned)線程中執行完成時,子線程會調用一個回調方法,該方法在調用EndInvoke()之前處理非同步方法呼叫的調用結果.
注意:原始線程可稱為父線程,由其產生(spawn)的線程可稱為子線程.對於BeginInvoke()有一些資訊必須知道:
1>當調用BeginInvoke時參數列表含以下的實際參數:
a:被委託引用的函數所需要的參數b: 另外的兩個參數: callback和state參數;
2>BeginInvoke()從線程池中取出一個線程並啟動引用的方法在新 線程中運行.
3>BeginInvoke()返還給調用線程一個實現了IAsyncResult介面 的 對象 。此介面的引用含有當前執行的非同步方法呼叫的執行狀態資訊。
初始線程之後繼續執行。
考慮現在很被人鼓吹的Ajax也只不過如這裡的思想.
EndInvoke()方法被用於提取由非同步執行的方法調用的傳回值.且被線程用於釋放資源。EndInvoke 有以下特性:
1>它會用一個 指向IAsyncResult的引用作為參數,IAsyncResult就是BeginInvoke的返回 值,並找到它(IAsyncResult)引用的線程。
2>如果線程池中那個線程已經退出,EndInvoke作如下的事情:
a:清理已退出的線程釋放其所佔資源。b:找到相應方法的傳回值並 將其作為自己的傳回值。
如果在EndInvoke調用時,線程池中的那個線程依舊在運行,調用者線程(父線程)會停止並等待它直到完成清理和傳回值。因為EndInvoke會對產生的線程做清理工作,所以必須保證針對每一個BeginInvoke()都調用了EndInvoke()(如果你不想資源流失的話);
3>如果非同步方法呼叫觸發了一個異常,當調用EndInvoke()時異常會被激發.EndInvoke提供所有源自非同步方法呼叫的輸出,包括ref和out參數.如果委託的相應方法有ref和out參數,它們必須在EndInvoke()方法的參數中出現,且先於IAsyncResult的位置:delegate.EndInvoke(out somePara, iAsyncResult);
等待直到完成模式:
namespace WaitUtilDone
{
public delegate string AsycDelegate(out string outArg, string inArg);//委託定義
class Program
{
static void Main(string[] args)
{
AsycDelegate myAsycDelegate = (AsycDelegate)AsyncWorker.AsyncMethod;//定義一個委託成員變數,這裡用的是簡潔形式
string fromAsyncMethrod;
IAsyncResult iAsyncRsl= myAsycDelegate.BeginInvoke(out fromAsyncMethrod, "toAsycMethod", null, null);
//主線程中........可在此段時間做其他的事Thread.Sleep(2000);
//主線程休息2秒鐘
string returnStrFromAsyncMethod = myAsycDelegate.EndInvoke(out fromAsyncMethrod,iAsyncRsl );
System.Console.WriteLine("the string return from AsyncMethod is {0}",returnStrFromAsyncMethod );
System.Console.WriteLine("the fromAsyncMethod is :{0}",fromAsyncMethrod);
System.Console.ReadLine();
}
}
class AsyncWorker {
public static string AsyncMethod(out string fromThisMethod,string toThisMethod) {
System.Console.WriteLine("<< enter the AsyncMethod!");
fromThisMethod = "string form AsyncWork.AsyncMehod as a out parameter";
System.Console.WriteLine("傳入此非同步方法呼叫的參數:{0}",toThisMethod );
Thread.Sleep(6000);//該方法會阻塞運行此方法的線程六秒
System.Console.WriteLine("end the AsyncMehod >>");
return "returnValueFromAsyncMethod!";
}
}
}
!關於IAsyncResult:它是BeginInvoke()和EndInvoke()不可或缺的部分.BeginInvoke方法會返回一個AsyncResult類型的對象引用,該類AsyncResult實現了ISyncResult介面,AsyncReslut表示非同步方法呼叫的狀態,對該類需要知道以下重要特徵:
1>當調用委派物件的BeginInvoke方法時系統建立一個該類的執行個體,將此對象的引用返回給ISyncResult 介面.
2> AsyncResult對象 含有一AsyncDelegate的屬性,它返回一個指向調用非同步方法呼叫的委託的引用,該屬性是AsyncResult 的屬性而不是IAyncResult介面的屬性.
3>IsCompleted屬性返回一個布爾值,表示非同步方法呼叫是否被執行完畢.
4>AsyncState屬性返回一個作對象的引用,它會作為BeginInvoke方法的調用參數返回的引用是個Object類型.回調模式會用到它.
@@輪詢模式:
輪詢模式中,原線程初始一個非同步方法呼叫的調用後,做一些額外的其他工作,然後周期性的檢查 IAsyncResult 所指對象的Isompleted,看非同步方法呼叫是否完成執行,如果完成,則會調用EndInvoke 並繼續執行,否則做一些其他工作,並繼續檢查.:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace PoolingPattern
{
public delegate string AsyncDelegate();
class Program
{
static string AsycMethod() {
System.Console.WriteLine("<< enter the asyncMethod.....");
Thread.Sleep(3000);
System.Console.WriteLine("......end the asyncMethod>>");
return "returnValueFromTheAsyncMethod";
}
static void Main(string[] args)
{
AsyncDelegate myAsyncDele = new AsyncDelegate(AsycMethod );//構造一個委託
System.Console.WriteLine("main方法中開始 調用 非同步方法呼叫");
IAsyncResult iAsyncRslt = myAsyncDele.BeginInvoke(null,null);
while (!iAsyncRslt .IsCompleted ){
System.Console.WriteLine("主線程中檢查->:..非同步呼叫的方法仍沒有 完成...");
for (long i = 0; i <= 100000000;i++ )
;//做一些 自己的事,這裡是 空耗 cpu
}//輪詢 結束 時非同步方法呼叫應已經完成了
/*此時可提起非同步執行的結果了*/
string strFromAsyncMethod = myAsyncDele.EndInvoke(iAsyncRslt);
System.Console.WriteLine("非同步方法呼叫返回的結果:{0}",strFromAsyncMethod );
System.Console.WriteLine("main方法結束");
System.Console.ReadLine();
}
}
}
##前兩個模式中,原始線程(運行main()方法的線程)僅在卵生線程完成(等待或輪詢)時,提取非同步方法呼叫的結果並繼續主線程的運行。回調模式不同它們:父線程非同步呼叫委託後並不需等待或輪詢,當非同步方法呼叫完成執行時系統會調用一個回調方法去處理非同步方法呼叫的結果,並調用EndInvoke方法釋放相關資源。BeginInvoke方法的最後兩個參數就是用與回調目的:
BeginInvoke後兩個參數的第一個參數就是回呼函數的名稱(函數名稱可轉化為委託!!)。第二個參數state可是null或想傳給回呼函數的一個對象的引用,可通過方法的IAsyncResult參數(用其AsyncState屬性 )訪問該對象。參數的類型是object型使用時需顯式轉型為特定的型別。
@@回呼函數的簽名必須與AsyncCallback委託的形式一致,此形式要求回呼函數需要一個IAsyncResult類型的參數和空的傳回值。
void AsyncCallback( IAsyncResult iar );
有多種將回呼函數提供給BeginInvoke方法的途徑。因為回調參數在BeginInvoke中是類型為AsyncCallback委託類型,你可以直接提供一個函數名,或是先構造一個委派物件。記住特定格式的函數的函數名總是可被編譯器隱式轉換為一個相應的委託。以下為我寫的小練習:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace CallBackPattern
{
class Program
{
static void callBackMethod(IAsyncResult asyncRslt) {
System.Console.WriteLine("<<...Enter the CallbackMethod");
System.Console.WriteLine("此回呼函數在非同步方法呼叫執行結束後調用的");
AsyncResult myAsynResult = (AsyncResult)asyncRslt;
EventHandler asyncDele = (EventHandler)myAsynResult.AsyncDelegate;
asyncDele.EndInvoke(asyncRslt);//資源回收見下面解釋
System.Console.WriteLine("End the CallbackMethod..>>");
}
static void myAsyncMethod (object source,System .EventArgs evtArgs){
System.Console.WriteLine("《《進入非同步方法呼叫調用地區");
Thread.Sleep(5000);
System.Console.WriteLine("結束非同步方法呼叫的調用地區》》");
}
static void Main(string[] args)
{
System.Console.WriteLine("進入主線程....");
EventHandler myAsyncDelegate = (EventHandler)myAsyncMethod;/*偷懶了,用的是系統提供的委託,注意方法名可
轉換為一個委託*/
IAsyncResult iAsyncRslt= myAsyncDelegate.BeginInvoke(null,null,callBackMethod ,null);
//使用隱式轉換等價下面的方式
//AsyncCallback myCallback = new AsyncCallback(callBackMethod);
// IAsyncResult iAsyncRslt= myAsyncDelegate.BeginInvoke(null,null,callBackMethod ,null);
System.Console.WriteLine("主線程結束,等待回調方法被調用.....");
System.Console.ReadLine();
}
}
}
**在回調方法中應該調用EndInvoke方法。且應該處理非同步方法呼叫的傳回值和out參數(如果有的話!)為了調用非同步方法呼叫的EndInvoke
方法你需要有一個指向非同步方法呼叫的委託引用,它在初始線程中,而不在卵生線程中,如果沒有使用BeginInvoke的state參數做任何事,你就可用其傳遞委託的引用給回調方法:IAsyncResult iar = delegate.BeginInvoke(args,CallWhenDone, delegate);在回呼函數中可通過IAsyncResult類型的參數擷取委託的引用。注意:IAsyncResult介面實際指向的是AsyncResult類的執行個體。儘管介面不含委託引用但AsyncResult類的對象持有委託的引用。AsyncResult類位於System.Runtime.Remoting.Messaging命名空間。當通過AsyncResult執行個體的AsyncDelegate屬性擷取委託並將之進行合適轉型。此後便可調用EndInvoke方法。
using System.Runtime.Remoting.Messaging; // 包含 AsyncResult類
void CallWhenDone( IAsyncResult iar )
{
AsyncResult ar = (AsyncResult) iar; //將介面轉型為AsyncResult執行個體
MyDel del = (MyDel) ar.AsyncDelegate; //擷取委託並轉型為適當的委託
long Sum = del.EndInvoke( iar ); // 調用EndInvoke
...
}
.........至此委託的非同步呼叫介紹算比較完整了。三個主要模式需仔細掌握,特別是回調模式,很不好理解。