舉個(gè)例子也許就能夠說(shuō)清楚回調(diào)定時(shí)器的用途。假設(shè)我的訂單系統(tǒng)接收各種不同類(lèi)型的訂單,當(dāng)訂單 A 進(jìn)來(lái)時(shí),系統(tǒng)根據(jù)訂單的類(lèi)型和其它特征進(jìn)行綜合判斷后,決定 A 訂單要在 2 秒之后被方法 M1 處理;接下來(lái)收到的 B 訂單經(jīng)過(guò)同樣的判斷后,決定要在 10 秒后被方法 M2 處理, …… 。這時(shí)候就可以用回調(diào)定時(shí)器來(lái)管理這些將要被延遲一定時(shí)間再執(zhí)行的任務(wù)。
當(dāng)然,我們可以使用定時(shí)器或前面介紹的循環(huán)引擎來(lái)實(shí)現(xiàn)這樣的功能,只不過(guò)我們自己需要手動(dòng)管理注冊(cè)的定時(shí)回調(diào)任務(wù),并且定時(shí)檢查每一個(gè)未處理訂單是否已經(jīng)到了處理的時(shí)刻。而回調(diào)定時(shí)器已經(jīng)自動(dòng)幫我們做好了這些事情,而且還是一個(gè)與具體應(yīng)用無(wú)關(guān)的通用組件,我們不需要再重復(fù)實(shí)現(xiàn)一個(gè)特定的類(lèi)來(lái)做這件事情。
回調(diào)定時(shí)器的形象示意圖如下:
設(shè)計(jì)回調(diào)定時(shí)器 ESBasic.Threading.Timers.ICallbackTimer 的主要是為了解決類(lèi)似下面的問(wèn)題:
(1) 任務(wù)(回調(diào))需要被延遲某一個(gè)時(shí)間間隔后執(zhí)行。
(2) 不同的任務(wù)需要被延遲的時(shí)間間隔可能不同。
(3) 不同的任務(wù)需要被處理的方式可能不同。
(4) 回調(diào)的延遲不需要非常精確。
回調(diào)定時(shí)器要解決的最主要問(wèn)題是第一點(diǎn),后面兩點(diǎn)也是回調(diào)定時(shí)器支持的重要特性。
3 .設(shè)計(jì)思想與實(shí)現(xiàn)
ICallbackTimer 接口的定義如下:
/// ICallbackTimer回調(diào)定時(shí)器。
/// 注意:回調(diào)任務(wù)會(huì)異步在ThreadPool的WorkerThread上執(zhí)行。即使目標(biāo)任務(wù)拋出異常也不會(huì)影響INotifyTimer的繼續(xù)運(yùn)行。
/// </summary>
public interface ICallbackTimer < T > : ICycleEngine
{
int TaskCount{ get ;}
/// <summary>
/// AddCallback添加一個(gè)回調(diào)任務(wù)。目標(biāo)任務(wù)會(huì)在spanInSecs后運(yùn)行。僅僅運(yùn)行一次。
/// </summary>
/// <paramname="spanInSecs"> 多少秒后執(zhí)行任務(wù) </param>
/// <paramname="_callback"> 目標(biāo)方法的委托 </param>
/// <paramname="_callbackPara"> 調(diào)用目標(biāo)方法的參數(shù) </param>
/// <returns> 新的任務(wù)編號(hào) </returns>
int AddCallback( int spanInSecs, CbGeneric < T > _callback,T_callbackPara);
/// <summary>
/// RemoveCallback刪除目標(biāo)回調(diào)任務(wù)。
/// </summary>
void RemoveCallback( int taskID);
/// <summary>
/// RemoveCallbackAndAddNew刪除目標(biāo)回調(diào)任務(wù),并添加一個(gè)新的回調(diào)任務(wù)。
/// </summary>
int RemoveCallbackAndAddNew( int taskIDToRemoved, int spanInSecs, CbGeneric < T > _newCallback,T_newCallbackPara);
/// <summary>
/// GetLeftSeconds離目標(biāo)任務(wù)被回調(diào)執(zhí)行還有多長(zhǎng)時(shí)間(s)。返回0,表示任務(wù)不存在或者任務(wù)已經(jīng)被執(zhí)行。
/// </summary>
int GetLeftSeconds( int taskID);
/// <summary>
/// Clear清除所有回調(diào)任務(wù)。
/// </summary>
void Clear();
}
根據(jù)上述對(duì)回調(diào)定時(shí)器的描述,我們可以借助循環(huán)引擎來(lái)實(shí)現(xiàn)它。你已經(jīng)看到, ICallbackTimer 繼承了 ICycleEngine 接口,這說(shuō)明我們可以通過(guò) Start 、 Stop 方法來(lái)控制回調(diào)定時(shí)器的運(yùn)行,并通過(guò) DetectSpanInSecs 屬性來(lái)設(shè)置檢測(cè)任務(wù)狀態(tài)的時(shí)間間隔,當(dāng)然, DetectSpanInSecs 設(shè)置的值最好小于最小的回調(diào)任務(wù)的延遲時(shí)間間隔。
ICallbackTimer 接口的泛型參數(shù) T ,代表的是回調(diào)執(zhí)行時(shí)所用到的參數(shù)的類(lèi)型。而回調(diào)方法的簽名必須是只接受一個(gè) T 類(lèi)型的參數(shù),并且沒(méi)有返回值(即如泛型委托 CbGeneric<T> )。
AddCallback 方法返回一個(gè) int ,表示添加的任務(wù)的唯一編號(hào),我們可以通過(guò)這個(gè)編號(hào)來(lái)查詢(xún)?cè)撊蝿?wù)離被回調(diào)執(zhí)行的時(shí)間( GetLeftSeconds 方法),或者根據(jù)該編號(hào)來(lái)取消目標(biāo)回調(diào)任務(wù)的執(zhí)行( RemoveCallback 方法)。
CallbackTimer 實(shí)現(xiàn)了 ICallbackTimer 接口,其實(shí)現(xiàn)要注意以下幾點(diǎn):
(1) CallbackTimer 繼承自 BaseCycleEngine ,它借助于循環(huán)引擎來(lái)進(jìn)行任務(wù)狀態(tài)的循環(huán)檢測(cè)。
(2) 為了允許在多線(xiàn)程的環(huán)境中回調(diào)定時(shí)器, CallbackTimer 必須對(duì)內(nèi)部集合( dicTask )進(jìn)行加鎖控制。
(3) CallbackTimer 使用 CallbackTask 類(lèi)來(lái)將一個(gè)定時(shí)回調(diào)任務(wù)封裝起來(lái)。
(4) 回調(diào)任務(wù)是異步在后臺(tái)的 ThreadPool 的 WorkerThread 上執(zhí)行的。所以達(dá)到執(zhí)行條件的多個(gè)回調(diào)任務(wù)不會(huì)相互阻塞,而是幾乎同時(shí)執(zhí)行的。
(5)
在
DoDetect
方法的實(shí)現(xiàn)中,我們先拷貝一份任務(wù)列表,然后再對(duì)其作
foreach
,而不是直接
this.dicTask.Values
作
foreach
,這是因?yàn)椋绻谀硞€(gè)回調(diào)執(zhí)行時(shí),調(diào)用了
AddCallback/RemoveCallback
將修改
this.dicTask
的內(nèi)容,而此時(shí)對(duì)
this.dicTask.Values
的
foreach
還未結(jié)束,這時(shí)
foreach
將拋出異常。
4. 使用時(shí)的注意事項(xiàng)
首先,是要注意 GetLeftSeconds 返回的值是不精確的。因?yàn)? CallbackTimer 是在每次循環(huán)檢測(cè)的時(shí)候(覆寫(xiě)基類(lèi)的 DoDetect 方法),修改每個(gè) CallbackTask 的 LeftSeconds 的(通過(guò) CallbackTask 的 SecondsPassed 方法)。所以, GetLeftSeconds 方法返回的只是一個(gè)大概的而非精確的值。
其次,回調(diào)的執(zhí)行是“一次性的”,即注冊(cè)的一個(gè)回調(diào)任務(wù)只會(huì)被執(zhí)行一次,或者被取消。
再次,回調(diào)任務(wù)定時(shí)器允許回調(diào)任務(wù)執(zhí)行時(shí)拋出異常,只不過(guò)該異常會(huì)被回調(diào)定時(shí)器忽略。所以,如果你要處理該異常,就應(yīng)該在回調(diào)方法中捕獲這個(gè)異常。
最后,由于回調(diào)任務(wù)是異步在后臺(tái)線(xiàn)程池中運(yùn)行的,所以如果同時(shí)被執(zhí)行的回調(diào)的任務(wù)很多,其數(shù)量超過(guò)了后臺(tái)線(xiàn)程池中的線(xiàn)程數(shù)量,此時(shí)就會(huì)導(dǎo)致某些回調(diào)任務(wù)將會(huì)被進(jìn)一步延遲執(zhí)行。
5. 擴(kuò)展
回調(diào)定時(shí)器
ICallbackTimer
暫時(shí)沒(méi)有任何擴(kuò)展。
注:ESBasic源碼可到
http://esbasic.codeplex.com/
下載。
ESBasic 可復(fù)用的.NET類(lèi)庫(kù)(07) -- 回調(diào)定時(shí)器ICallbackTimer
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
