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

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