System.Threading.ThreadPool 類
System.Threading.Timer 類
如果線程的數(shù)目并不是很多,而且你想控制每個線程的細(xì)節(jié)諸如線程的優(yōu)先級等,使用Thread是比較合適的;但是如果有大量的線程,考慮使用線程池應(yīng)該更好一些,它提供了高效的線程管理機制來處理多任務(wù)。 對于定期的執(zhí)行任務(wù)Timer類是合適的;使用代表是異步方法調(diào)用的首選。
System.Threading.ThreadPool Class 當(dāng)你創(chuàng)建應(yīng)用程序時,你應(yīng)該認(rèn)識到大部分時間你的線程在空閑的等待某些事件的發(fā)生(諸如按下一個鍵或偵聽套節(jié)子的請求)。毫無疑問的,你也會認(rèn)為這是絕對的浪費資源。
如果這里有很多的任務(wù)需要完成,每個任務(wù)需要一個線程,你應(yīng)該考慮使用線程池來更有效的管理你的資源并且從中受益。線程池是執(zhí)行的多個線程集合,它允許你添加以線程自動創(chuàng)建和開始的任務(wù)到隊列里面去。使用線程池使得你的系統(tǒng)可以優(yōu)化線程在CPU使用時的時間碎片。但是要記住在任何特定的時間點,每一個進(jìn)程和每個線程池只有一個一個正在運行的線程。這個類使得你的線程組成的池可以被系統(tǒng)管理,而使你的主要精力集中在工作流的邏輯而不是線程的管理。
當(dāng)?shù)谝淮螌嵗疶hreadPool類時線程池將被創(chuàng)建。它有一個默認(rèn)的上限,即每處理器最多可以有25個,但是這個上限是可以改變的。這樣使得處理器不會閑置下來。如果其中一個線程等待某個事件的發(fā)生,線程池將初始化另外一個線程并投入處理器工作,線程池就是這樣不停的創(chuàng)建工作的線程和分配任務(wù)給那些沒有工作的在隊列里的線程。唯一的限制是工作線程的數(shù)目不能超過最大允許的數(shù)目。每個線程將運行在默認(rèn)的優(yōu)先級和使用默認(rèn)的屬于多線程空間的堆棧大小空間。一旦一項工作任務(wù)被加入隊列,你是不能取消的。
請求線程池處理一個任務(wù)或者工作項可以調(diào)用QueueUserWorkItem方法。這個方法帶一個WaitCallback代表類型的參數(shù),這個參數(shù)包裝了你藥完成的任務(wù)。運行時自動為每一個的任務(wù)創(chuàng)建線程并且在任務(wù)釋放時釋放線程。
下面的代碼說明了如何創(chuàng)建線程池和怎樣添加任務(wù):
public?void?afunction(object?o)?



{?

//?do?what?ever?the?function?is?supposed?to?do.?

}?

//thread?entry?code?



{?

//?create?an?instance?of?WaitCallback?

WaitCallback?myCallback?=?new?WaitCallback?(afunction);?

//add?this?to?the?thread?pool?/?queue?a?task?

ThreadPool.QueueUserWorkItem?(myCallback);?

}


你也可以通過調(diào)用ThreadPool.RegisterWaitForSingleObject方法來傳遞一個System.Threading.WaitHandle,當(dāng)被通知或者時間超過了調(diào)用被System.Threading.WaitOrTimerCallback包裝的方法。
線程池和基于事件的編程模式使得線程池對注冊的WaitHandles的監(jiān)控和對合適的WaitOrTimerCallback代表方法的調(diào)用十分簡單(當(dāng)WaitHandle被釋放時)。這些做法其實很簡單。這里有一個線程不斷的觀測在線程池隊列等待操作的狀態(tài)。一旦等待操作完成,一個線程將被執(zhí)行與其對應(yīng)的任務(wù)。因此,這個方法隨著出發(fā)觸發(fā)事件的發(fā)生而增加一個線程。
讓我們看看怎么隨事件添加一個線程到線程池,其實很簡單。我們只需要創(chuàng)建一個ManualResetEvent類的事件和一個WaitOrTimerCallback的代表,然后我們需要一個攜帶代表狀態(tài)的對象,同時我們也要決定休息間隔和執(zhí)行方式。我們將上面的都添加到線程池,并且激發(fā)一個事件:
public?void?afunction(object?o)?



{?

//?do?what?ever?the?function?is?supposed?to?do.?

}?


//object?that?will?carry?the?status?information

public?class?anObject?



{?

}?

//thread?entry?code?



{?

//create?an?event?object

ManualResetEvent?aevent?=?new?ManualResetEvent?(false);?


//?create?an?instance?of?WaitOrTimerCallback?

WaitOrTimerCallback?thread_method?=?new?WaitOrTimerCallback?(afunction);?


//?create?an?instance?of?anObject?

anObject?myobj?=?new?anObject();?


//?decide?how?thread?will?perform?

int?timeout_interval?=?100;?//?timeout?in?milli-seconds.?

bool?onetime_exec?=?true;?


//add?all?this?to?the?thread?pool.?

ThreadPool.?RegisterWaitForSingleObject?(aevent,?thread_method,?myobj,?timeout_interval,?onetime_exec);?


//?raise?the?event?

aevent.Set();?

}


在QueueUserWorkItem和RegisterWaitForSingleObject方法中,線程池創(chuàng)建了一個后臺的線程來回調(diào)。當(dāng)線程池開始執(zhí)行一個任務(wù),兩個方法都將調(diào)用者的堆棧合并到線程池的線程堆棧中。如果需要安全檢查將耗費更多的時間和增加系統(tǒng)的負(fù)擔(dān),因此可以通過使用它們對應(yīng)的不安全的方法來避免安全檢查。就是ThreadPool.UnsafeRegisterWaitForSingleObject 和ThreadPool.UnsafeQueueUserWorkItem。
你也可以對與等待操作無關(guān)的任務(wù)排隊。 Timer-queue timers and registered wait operations也使用線程池。它們的返回方法也被放入線程池排隊。
線程池是非常有用的,被廣泛的用于。NET平臺上的套節(jié)子編程,等待操作注冊,進(jìn)程計時器和異步的I/O。對于小而短的任務(wù),線程池提供的機制也是十分便利處于多線程的。線程池對于完成許多獨立的任務(wù)而且不需要逐個的設(shè)置線程屬性是十分便利的。但是,你也應(yīng)該很清楚,有很多的情況是可以用其他的方法來替代線程池的。比如說你的計劃任務(wù)或給每個線程特定的屬性,或者你需要將線程放入單個線程的空間(而線程池是將所有的線程放入一個多線程空間),抑或是一個特定的任務(wù)是很冗長的,這些情況你最好考慮清楚,安全的辦法比用線程池應(yīng)該是你的選擇。
System.Threading.Timer Class
Timer類對于周期性的在分離的線程執(zhí)行任務(wù)是非常有效的,它不能被繼承。
這個類尤其用來開發(fā)控制臺應(yīng)用程序,因為System.Windows.Forms.Time是不可用的。比如同來備份文件和檢查數(shù)據(jù)庫的一致性。
當(dāng)創(chuàng)建Timer對象時,你藥估計在第一個代理調(diào)用之前等待的時間和后來的每次成功調(diào)用之間的時間。一個定時調(diào)用發(fā)生在方法的應(yīng)得時間過去,并且在后來周期性的調(diào)用這個方法。你可以適應(yīng)Timer的Change方法來改變這些設(shè)置的值或者使Timer失效。當(dāng)定時器Timer不再使用時,你應(yīng)該調(diào)用Dispose方法來釋放其資源。
TimerCallback代表負(fù)責(zé)指定與Timer對象相關(guān)聯(lián)的方法(就是要周期執(zhí)行的任務(wù))和狀態(tài)。它在方法應(yīng)得的時間過去之后調(diào)用一次并且周期性的調(diào)用這個方法直到調(diào)用了Dispose方法釋放了Timer的所有資源。系統(tǒng)自動分配分離的線程。
讓我們來看一段代碼看看事如何創(chuàng)建Timer對象和使用它的。我們首先要創(chuàng)建一個TimerCallback代理,在后面的方法中要使用到的。如果需要,下一步我們要創(chuàng)建一個狀態(tài)對象,它擁有與被代理調(diào)用的方法相關(guān)聯(lián)的特定信息。為了使這些簡單一些,我們傳遞一個空參數(shù)。我們將實例化一個Timer對象,然后再使用Change方法改變Timer的設(shè)置,最后調(diào)用Dispose方法釋放資源。
//?class?that?will?be?called?by?the?Timer?

public?class?WorkonTimerReq?



{?

public?void?aTimerCallMethod()?



{?

//?does?some?work

}?

}?


//timer?creation?block?



{?

//instantiating?the?class?that?gets?called?by?the?Timer.?

WorkonTimerReq?anObj?=?new?WorkonTimerReq?()?;?


//?callback?delegate?

TimerCallback?tcallback?=?new?TimerCallback(anObj.?aTimerCallMethod)?;?


//?define?the?dueTime?and?period?

long?dTime?=?20?;?//?wait?before?the?first?tick?(in?ms)?

long?pTime?=?150?;?//?timer?during?subsequent?invocations?(in?ms)?


//?instantiate?the?Timer?object?

Timer?atimer?=?new?Timer(tcallback,?null,?dTime,?pTime)?;?


//?do?some?thing?with?the?timer?object?


?

//change?the?dueTime?and?period?of?the?Timer?

dTime=100;?

pTime=300;?

atimer.Change(dTime,?pTime)?;?

//?do?some?thing?


?

atimer.Dispose()?;?


?

}


異步編程
這部分內(nèi)容如果要講清楚本來就是很大的一部分,在這里,我不打算詳細(xì)討論這個東西,我們只是需要直到它是什么,因為多線程編程如果忽律異步的多線程編程顯然是不應(yīng)該的。異步的多線程編程是你的程序可能會用到的另外一種多線程編程方法。
在前面的文章我們花了很大的篇幅來介紹線程的同步和怎么實現(xiàn)線程的同步,但是它有一個固有的致命的缺點,你或許注意到了這一點。那就是每個線程必須作同步調(diào)用,也就是等到其他的功能完成,否則就阻塞。當(dāng)然,某些情況下,對于那些邏輯上相互依賴的任務(wù)來說是足夠的。異步編程允許更加復(fù)雜的靈活性。一個線程可以作異步調(diào)用,不需要等待其他的東西。你可以使用這些線程作任何的任務(wù),線程負(fù)責(zé)獲取結(jié)果推進(jìn)運行。這給予了那些需要管理數(shù)目巨大的請求而且負(fù)擔(dān)不起請求等待代價的企業(yè)級的系統(tǒng)更好的可伸縮性。