Timer定時器的設(shè)計(jì)方法
在系統(tǒng)開發(fā)過程中經(jīng)常用到定時器進(jìn)行定時處理,比如比較常見的郵件群發(fā)、實(shí)時更新論壇的在線人數(shù)、文章數(shù)、點(diǎn)擊率等。 很多情況下,我們不能對某一狀態(tài)或者某一行為進(jìn)行實(shí)時監(jiān)控,所以就希望系統(tǒng)能夠?qū)崿F(xiàn)這一功能。通過多線程技術(shù)可以使得定時器的性能更高。
盡管定時器能夠自動處理或者一些批處理操作,但是定時器也給系統(tǒng)帶來一定的安全隱患,特別是當(dāng)定時進(jìn)行的操作出現(xiàn)bug時,如果沒有對Exception做出及時的處理,系統(tǒng)資源將會大大的浪費(fèi),嚴(yán)重的情況下,可能導(dǎo)致系統(tǒng)崩潰。因此,對于定時器的使用一定要慎重,至少要保證定時處理的行為出現(xiàn)異常的可能性很小,并在出現(xiàn)Exception的情況下及時處理。
System.Threading.Timer 是一個非常常用的定時器類,是一個使用回調(diào)方法的計(jì)時器,而且由線程池線程服務(wù),簡單且對資源要求不高。
public Timer (
TimerCallback callback,
Object state,
TimeSpan dueTime,
TimeSpan period
)
參數(shù)
callback
一個 TimerCallback 委托,表示要執(zhí)行的方法。
state
一個包含回調(diào)方法要使用的信息的對象,或者為 空引用(在 Visual Basic 中為 Nothing)。
dueTime
TimeSpan,表示在 callback 參數(shù)調(diào)用它的方法之前延遲的時間量。指定 -1 毫秒以防止啟動計(jì)時器。指定零 (0) 以立即啟動計(jì)時器。
period
在調(diào)用 callback 所引用的方法之間的時間間隔。指定 -1 毫秒可以禁用定期終止。
方法、原理
使用 TimerCallback 委托指定希望 Timer 執(zhí)行的方法。計(jì)時器委托在構(gòu)造計(jì)時器時指定,并且不能更改。此方法不在創(chuàng)建計(jì)時器的線程上執(zhí)行,而是在系統(tǒng)提供的 ThreadPool 線程上執(zhí)行。
創(chuàng)建計(jì)時器時,可以指定在第一次執(zhí)行方法之前等待的時間量(截止時間)以及此后的執(zhí)行期間等待的時間量(時間周期)??梢允褂?Change 方法更改這些值或禁用計(jì)時器。
當(dāng)不再需要計(jì)時器時,請使用 Dispose 方法釋放計(jì)時器持有的資源。如果希望在計(jì)時器被釋放時接收到信號,請使用接受 WaitHandle 的 Dispose(WaitHandle) 方法重載。計(jì)時器已被釋放后,WaitHandle 便終止。
由計(jì)時器執(zhí)行的回調(diào)方法應(yīng)該是可重入的,因?yàn)樗窃?ThreadPool 線程上調(diào)用的。
備注:
在超過 dueTime 以后及此后每隔 period 時間間隔,都會調(diào)用一次 callback 參數(shù)所指定的委托。
如果 dueTime 為零 (0),則立即調(diào)用 callback。如果 dueTime 是 -1 毫秒,則不會調(diào)用 callback;計(jì)時器將被禁用,但通過調(diào)用 Change 方法可以重新啟用計(jì)時器。
如果 period 為零 (0) 或 -1 毫秒,而且 dueTime 為正,則只會調(diào)用一次 callback;計(jì)時器的定期行為將被禁用,但通過使用 Change 方法可以重新啟用該行為。
最簡單的定時器
using System;
using System.Threading;
public class TestTimer
{
/**////
/// 定時器
///
private Timer iTimer;
/**////
/// constructor
///
public TestTimer()
{
iTimer = new System.Threading.Timer(new TimerCallback(Doing));
iTimer.Change(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
/**////
///
///
///
public void Doing(object nObject)
{
//do something
}
}
一個比較完整的計(jì)時器:
下面是我設(shè)計(jì)的一個簡單實(shí)例。在一個問卷調(diào)查系統(tǒng)中,每一張問卷都有其終止日期,當(dāng)?shù)竭_(dá)了終止日期時,需要系統(tǒng)自動將其關(guān)閉。這就需要定時器對問卷的狀態(tài)和終止日期進(jìn)行實(shí)時監(jiān)控,及時關(guān)閉。這里采用了一個簡單的單件模式來操作、控制定時器。 這里主要的操作包括定時器開始、終止、執(zhí)行一次。
PaperManager管理類
/**////
/// 管理類
///
public class PaperManager
{
/**////
/// 定時器
///
private Timer iTimer;
/**////
/// 啟動時間
///
private TimeSpan dueTime;
/**////
/// 方法調(diào)用間隔
///
private TimeSpan period;[!--empirenews.page--]
/**////
/// 委托
///
private TimerCallback timerDelegate;
/**////
/// 靜態(tài)實(shí)例
///
private static readonly PaperManager self = new PaperManager();
/**////
/// 構(gòu)造函數(shù)
///
public PaperManager()
{
timerDelegate = new TimerCallback(CheckStatus);
}
/**////
///
///
///
public static PaperManager getInstance()
{
return self;
}
/**////
/// 設(shè)置啟動時間間隔
///
/// 天
/// 小時
/// 分鐘
/// 秒
/// 毫秒
public void setDueTime(int days, int hours, int minutes, int seconds, int milisecond)
{
dueTime = new TimeSpan(days, hours, minutes, seconds, milisecond);
}
/**////
/// 設(shè)置回調(diào)時間間隔
///
/// 天
/// 小時
/// 分鐘
/// 秒
/// 毫秒
public void setPeriod(int days, int hours, int minutes, int seconds, int milisecond)
{
period = new TimeSpan(days, hours, minutes, seconds, milisecond);
}
/**////
/// 開始
///
public void Start()
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
dueTime = TimeSpan.FromSeconds(0);
period = TimeSpan.FromSeconds(10);
iTimer = new Timer(timerDelegate, autoEvent, dueTime, period);
autoEvent.WaitOne(5000, false);
iTimer.Change(dueTime, period);
}
/**////
/// 停止
///
public void Stop()
{
iTimer.Dispose();
}
/**////
/// 執(zhí)行一次
///
public void ExcuteOneTime()
{
if (iTimer != null)
{
iTimer.Dispose();
}
//如果 period 為零 (0) 或 -1 毫秒,而且 dueTime 為正,則只會調(diào)用一次 callback;
//計(jì)時器的定期行為將被禁用,但通過使用 Change 方法可以重新啟用該行為。
setDueTime(0, 0, 0, 0, 1);
setPeriod(0, 0, 0, 0, -1);
AutoResetEvent autoEvent = new AutoResetEvent(false);
iTimer = new Timer(timerDelegate, autoEvent, dueTime, period);
autoEvent.WaitOne(5000, false);
iTimer.Change(dueTime, period);
}
/**////
/// 行為
///
///
public void CheckStatus(object nObject)
{
AutoResetEvent autoEvent = (AutoResetEvent)nObject;[!--empirenews.page--]
if (ExcuteUpdate())
{
autoEvent.Set();
}
}
/**////
/// 更新
///
///
private bool ExcuteUpdate()
{
try
{
//應(yīng)該從數(shù)據(jù)庫獲得Paper對象的集合,這里簡略
//List
List
foreach (Paper item in paperList)
{
if (item.EndTime <= DateTime.Now)
{
if (item.Status == Paper.StatusOfNormal)
{
item.Status = Paper.StatusOfTerminate;
}
}
}
/**/////執(zhí)行數(shù)據(jù)更新,這里省略
return true;
}
catch
{
return false;
}
}
}
這是問卷的實(shí)體類,只是簡單的列出必要的屬性。
Paper實(shí)體類
/**////
/// 實(shí)體類
///
public class Paper
{
/**////
/// 終止時間
///
public DateTime EndTime;
/**////
/// 狀態(tài)
///
public int Status;
/**////
/// 正常
///
public const int StatusOfNormal = 1;
/**////
/// 終止
///
public const int StatusOfTerminate = 2;
/**////
///
///
///
///
public Paper(int status, DateTime endTime)
{
Status = status;
EndTime = endTime;
}
}