[英]Timer for a polling windows service
我編寫了一個Timer類,以便在輪詢另一個系統的Windows服務中使用它。 我這樣做是因為我遇到了System.Timers.Timer
沒有解決的兩個問題。
System.Timers.Timer.Stop
函數阻止主線程,直到Elapsed EventHanler的執行結束。 System.Timers.Timer
不處理事件重入。 我希望Interval介於兩個Elapsed EventHanler之間,這樣如果之前的調用(+ interval)還沒有完成,Timer就不會調用Elapsed EventHanler。 在編寫課程時,我發現我需要解決一些與thrading相關的問題,因為我不太熟悉那些我想知道以下Timer類是否是線程安全的?
public class Timer
{
System.Timers.Timer timer = new System.Timers.Timer() { AutoReset = false };
ManualResetEvent busy = new ManualResetEvent(true);
public double Interval
{
get { return timer.Interval; }
set { timer.Interval = value; }
}
public Timer()
{
timer.Elapsed += new ElapsedEventHandler(TimerElapsed);
}
void TimerElapsed(object sender, ElapsedEventArgs e)
{
try
{
busy.Reset();
OnElapsed(e);
timer.Start();
}
finally
{
busy.Set();
}
}
public event EventHandler Elapsed;
protected void OnElapsed(EventArgs e)
{
if (Elapsed != null)
{
Elapsed(this, e);
}
}
public virtual void Start()
{
busy.WaitOne();
timer.Start();
}
public virtual void Stop()
{
busy.WaitOne();
timer.Stop();
}
}
首先,你可以使用System.Threading.Timer而不是這個計時器,根據我的經驗,這是一個表現更好的計時器(只是個人經驗的建議)。
其次,在這種情況下,您應該給出一個標志,該標志在早期計時器完成任務后設置(此標志 - 所有線程都可以訪問的靜態字段)。
在這種情況下,請確保在出現任何錯誤的情況下,然后由您重置標志,以便其他計時器不會無限期等待,以防定時器任務由於內部發生錯誤而無法為其他定時器設置標志。任務(應添加一類最終塊以確保處理錯誤並始終重置標志)。
重置此標志后,下一個線程將對其進行處理,因此此檢查將確保所有線程逐個啟動任務。
我為這種情況編寫的示例代碼(方法代碼已被刪除,這將為您提供設計細節)。
namespace SMSPicker
{
public partial class SMSPicker : ServiceBase{
SendSMS smsClass;
AutoResetEvent autoEvent;
TimerCallback timerCallBack;
Timer timerThread;
public SMSPicker()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
smsClass = new SendSMS();
autoEvent = new AutoResetEvent(false);
long timePeriod = string.IsNullOrEmpty(ConfigurationSettings.AppSettings["timerDuration"]) ? 10000 : Convert.ToInt64(ConfigurationSettings.AppSettings["timerDuration"]);
timerCallBack = new TimerCallback(sendSMS);
timerThread = new Timer(timerCallBack, autoEvent, 0, timePeriod);
}
private void sendSMS(object stateInfo)
{
AutoResetEvent autoResetEvent = (AutoResetEvent)stateInfo;
smsClass.startSendingMessage();
autoResetEvent.Set();
}
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
smsClass.stopSendingMessage();
timerThread.Dispose();
}
}
}
namespace SMSPicker
{
class SendSMS
{
//This variable has been done in order to ensure that other thread does not work till this thread ends
bool taskDone = true;
public SendSMS()
{
}
//this method will start sending the messages by hitting the database
public void startSendingMessage()
{
if (!taskDone)
{
writeToLog("A Thread was already working on the same Priority.");
return;
}
try
{
}
catch (Exception ex)
{
writeToLog(ex.Message);
}
finally
{
taskDone = stopSendingMessage();
//this will ensure that till the database update is not fine till then, it will not leave trying to update the DB
while (!taskDone)//infinite looop will fire to ensure that the database is updated in every case
{
taskDone = stopSendingMessage();
}
}
}
public bool stopSendingMessage()
{
bool smsFlagUpdated = true;
try
{
}
catch (Exception ex)
{
writeToLog(ex.Message);
}
return smsFlagUpdated;
}
}
}
另一種方法是等待事件而不是使用計時器。
public class PollingService {private Thread _workerThread; 私有AutoResetEvent _finished; private const int _timeout = 60 * 1000;
public void StartPolling()
{
_workerThread = new Thread(Poll);
_finished = new AutoResetEvent(false);
_workerThread.Start();
}
private void Poll()
{
while (!_finished.WaitOne(_timeout))
{
//do the task
}
}
public void StopPolling()
{
_finished.Set();
_workerThread.Join();
}
}
在您的服務中
public partial class Service1 : ServiceBase
{
private readonly PollingService _pollingService = new PollingService();
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_pollingService.StartPolling();
}
protected override void OnStop()
{
_pollingService.StopPolling();
}
}
我有一個類似的問題:更簡單的事情可能是使用System.Threading.Timer與句點= 0;
在這種情況下,調用一次回調方法。 然后,重置計時器(調用其Change()方法)再次打開它; 在你重新發生的方法結束時。
此方案在此處解釋: http : //kristofverbiest.blogspot.com/2008/08/timers-executing-task-at-regular.html
您可以使用計時器或睡眠的專用線程/任務。 就個人而言,我發現專用線程/任務比這些事情的計時器更容易使用,因為它更容易控制輪詢間隔。 此外,您絕對應該使用TPL
提供的合作取消機制。 它沒有必要拋出異常。 只有在調用ThrowIfCancellationRequested
。 您可以使用IsCancellationRequested
來檢查取消令牌的狀態。
這是一個非常通用的模板,您可以使用它來開始。
public class YourService : ServiceBase
{
private CancellationTokenSource cts = new CancellationTokenSource();
private Task mainTask = null;
protected override void OnStart(string[] args)
{
mainTask = new Task(Poll, cts.Token, TaskCreationOptions.LongRunning);
mainTask.Start();
}
protected override void OnStop()
{
cts.Cancel();
mainTask.Wait();
}
private void Poll()
{
CancellationToken cancellation = cts.Token;
TimeSpan interval = TimeSpan.Zero;
while (!cancellation.WaitHandle.WaitOne(interval))
{
try
{
// Put your code to poll here.
// Occasionally check the cancellation state.
if (cancellation.IsCancellationRequested)
{
break;
}
interval = WaitAfterSuccessInterval;
}
catch (Exception caught)
{
// Log the exception.
interval = WaitAfterErrorInterval;
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.