2014-03-12 50 views
1

我開發的Windows服務必須在特定的確切時間執行一個方法。如何在C#中的特定時間(遠離現在)執行代碼?

服務啓動時,它從SQL Server數據庫中提取幾個不同的DateTime,然後應該爲每個DateTime啓動一個線程。每個線程都應該等到確切的DateTime值,然後做一些工作,然後再等一段時間,然後再做一些最後的工作。

我不想使用System.Timers.Timer因爲:

  • 的方法是在準確的指定時間
  • 這個確切的執行時間可能在24h以上
  • 我需要將一個參數傳遞給Elapsed事件

我該如何實現?

謝謝。

編輯:昨天晚上我只是有一個想法,如果我寫一個動態輪詢定時器呢?例如,如果到期日期時間超過12小時,它可以每小時輪詢一次以重新同步。然後,如果到期日期超過3小時,它可以每隔30分鐘輪詢一次,等等......你覺得怎麼樣?這會是一個糟糕的代碼嗎?

再次感謝您。

+1

計劃任務? –

+0

可能重複的[如何設置計時器在特定時間在c#中執行](http://stackoverflow.com/questions/21299214/how-to-set-timer-to-execute-at-specific-time-in- c-sharp) – itsme86

+0

對於這個問題,唯一正確的答案是「你不能」,因爲由於操作系統的限制,你無法將任何事情調度到毫秒級。話雖如此,爲什麼子彈1和2會阻止你使用'System.Timers.Timer'? –

回答

2

您無法將參數傳遞給System.Timers.Timer的tick事件,但您可以使用System.Threading.Timer。延遲時間「大於24小時」是沒有問題的。定時器使用以毫秒錶示的32位週期。 2^31毫秒可以達到24.85天。還有一個重載,可讓您使用64位有符號整數指定延遲。 2^63毫秒是...幾億年。

但是讓這些定時器在將來的確切日期/時間打勾是有問題的,因爲您必須指定延遲時間,並且時鐘更改會導致您的延遲太短或太長(認爲夏令時或用戶啓動的時間更改)。您可以指定滴答時間並讓計時器每秒鐘輪詢當前時間。這有點醜陋,但它起作用。

另一種選擇是使用Windows Waitable Timer對象,它可以讓您設置確切的日期和時間。不幸的是,該框架不包含相應的對象。幾年前我寫了一篇關於它的文章併發布了代碼。該文章不再在線提供,但您可以從http://mischel.com/pubs/waitabletimer.zip下載代碼。

+0

謝謝你的回答。 我同意有一個計時器每秒輪詢當前時間是一種醜陋的,但如果我寫一個動態輪詢計時器呢?例如,如果到期日期時間超過12小時,則它只能每小時輪詢一次。然後,如果到期日期時間超過3小時,它可以每隔30分鐘輪詢一次,依此類推... – gilgha

+0

@gilgha:如果每秒輪詢一次,或者使用某種算法調整輪詢頻率,這並不重要。資源使用真的不是問題,除非您的機器嚴重超載。投票是輪詢。在這種情況下反對它主要是審美的。 –

1

過去我已經使用了一種叫做ManualResetEvent的東西,並取得了很大的成功。假設您知道運行代碼的確切時間,那麼您將能夠計算預計運行時間(TTR),並且應該在第一次運行中配置後續運行。

partial class SomeService : ServiceBase 
{ 
    private ManualResetEvent stop = new ManualResetEvent(false); 
    private List<DateTime> times; 
    private int idxDT = 0; 

    public SomeService() 
    { 
     InitializeComponent(); 
    } 

    protected override void OnStart(string[] args) 
    { 
     this.stop.Reset(); 

     //implement you logic to calculate miliseconds to desired first run time 
     int miliseconds_to_run = 1; 

     ThreadPool.RegisterWaitForSingleObject(this.stop, 
               new WaitOrTimerCallback(ThreadFunc), 
               null, 
               miliseconds_to_run, 
               true); 

    } 

    private void ThreadFunc(object _state, bool _timedOut) 
    { 
     if (_timedOut) 
     { 
      if(this.times == null) 
      { 
       //get a list of times to run, store it along with the index of current TTR 
       this.times = new List<DateTime>(); 
      } 

      int miliseconds_to_run = (this.times[this.idxDT++] - DateTime.Now).Miliseconds; 

      ThreadPool.RegisterWaitForSingleObject(this.stop, 
                new WaitOrTimerCallback(ThreadFunc), 
                null, 
                miliseconds_to_run, 
                true); 
     } 
    } 


    protected override void OnStop() 
    { 
     this.stop.Set(); 
    } 
} 

當然,這很大程度上取決於您的工作開始時間的精確程度。 ThreadPool類將向操作系統發送一個線程調用請求,然後它將等待來自該線程池的下一個可用線程。在一些有很多線程的進程中,這可能會導致線程匱乏,您的確切時間將會延遲。

你也可以嘗試從.NET創建任務計劃程序作業,但我從來沒有這樣做過。

1

即使你說你不想使用System.Timers.Timer無論如何,我會告訴你如何去做,因爲問題中的子彈1和子彈2似乎並不是我認爲的使用它的有效點。

因此,這裏是我在我的幾個應用程序做:

// 1. Create the timer 
System.Timers.Timer timer = new System.Timers.Timer(); 
timer.AutoReset = false; 
timer.Elapsed += ...; 

// 2. Calculate the number of milliseconds between the scheduled time and the current time 
timer.Interval = (scheduledDate - DateTime.Now).TotalMilliSeconds; 

// 3. Start the timer 
timer.Start(); 

完成。該活動將在所需時間「大致」觸發。請注意,這不會精確到毫秒,因爲Windows不是實時操作系統。

1

我沒有面對類似的問題,並使用下面的解決方案,可能會解決您的問題。

我使用了ManualResetEvent類,添加了一個Wait方法並使服務等待確切的時間。

protected ManualResetEvent waitEvent = new ManualResetEvent(false); 

protected bool Wait(int milliSecs) 
    { 
     return !this.waitEvent.WaitOne(milliSecs, true); 
    } 

我用這個等待中,如下所示OnStart方法:

protected override void OnStart(string[] args) 
    { 

     DateTime nextRun = dt;//datetime from the database 

     YourProcessOrThreadToRun process = new YourProcessOrThreadToRun(); 
     while (Wait((int)nextRun.Subtract(DateTime.Now).TotalMilliseconds)) 
     { 
       process.StartProcess();     
     } 
    } 

YourProcessOrThreadToRun是要對準確時間運行的線程。

相關問題