2009-11-13 97 views
3

我編寫了一個Timer類,以便在輪詢另一個系統的Windows服務中使用它。我這樣做是因爲我有兩個問題,System.Timers.Timer沒有解決。投票窗口服務的計時器

  1. Elapsed EventHanler在後臺運行,因此如果主線程結束,它的執行將中止。我希望System.Timers.Timer.Stop函數可以阻止主線程,直到Elapsed EventHanler的執行結束。
  2. System.Timers.Timer不涉及事件再入境。我希望間隔在兩個Elapsed EventHanler之間,這樣如果前一個調用(+間隔)尚未完成,定時器就不會調用Elapsed EventHanler。

在寫我發現我需要一些thrading相關的問題,因爲我不是太那些我想知道,如果下面的Timer類是線程安全的經驗,以DAEL類?

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(); 
    } 
} 

回答

5

都可以使用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; 
    } 

} 
} 
0

我有一個類似的問題:更簡單的事情可以是使用System.Threading.Timer與period = 0;

在這種情況下,調用一次回調方法。然後,重置定時器(調用它的Change()方法)再次打開;在你重新發生的方法結束時。

這種情況在此說明:http://kristofverbiest.blogspot.com/2008/08/timers-executing-task-at-regular.html

+1

此解決方案不能解決1中描述的問題。 – Martin 2009-11-25 08:38:36

0

這樣做將是等待一個事件而不是使用計時器的另一種方式。

public class PollingService Private Thread _workerThread; private 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(); 
    } 

} 
0

你可以使用定時器或專用線程/任務睡覺或什麼的。就個人而言,我發現專用線程/任務比定時器更容易處理這些事情,因爲它更容易控制輪詢間隔。此外,您一定要使用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; 
     } 
    } 
    } 
}