[英]C#, System.Timers.Timer, run every 15min in sync with system clock
[英]run every min in sync with system clock (not working on Windows Server 2003)
我正在嘗試使計時器每分鍾運行一次,以與系統時鍾同步(00:01:00、00:02:00、00:03:00等)。 這是我的代碼。
private System.Timers.Timer timer;
public frmMain()
{
timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = GetInterval();
timer.Start();
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString("hh:mm:ss tt"));
timer.Interval = GetInterval();
timer.Start();
}
private double GetInterval()
{
DateTime now = DateTime.Now;
return ((60 - now.Second) * 1000 - now.Millisecond);
}
它可以在我的家用PC上完美運行。
12:12:00 AM
12:13:00 AM
12:14:00 AM
12:15:00 AM
12:16:00 AM
12:17:00 AM
12:18:00 AM
12:19:00 AM
12:20:00 AM
12:21:00 AM
但是我在VPS(Windows Server 2003)上得到了奇怪的結果。
12:11:59 AM
12:12:59 AM
12:13:00 AM
12:13:59 AM
12:14:00 AM
12:14:59 AM
12:15:00 AM
12:15:59 AM
12:16:00 AM
12:16:59 AM
12:17:00 AM
12:17:59 AM
12:18:00 AM
12:18:59 AM
12:19:00 AM
12:19:59 AM
12:20:00 AM
12:20:59 AM
12:21:00 AM
是否因為System.Timers.Timer在Windows Server 2003上無法正常工作? 還是我的VPS有問題?
諸如System.Timers.Timer
類的普通計時器不准確,並且不足以達到1毫秒的間隔。
首先,它們的內部更新速率為10-15毫秒。 其次,根據系統的不同,其他線程可能會運行約15毫秒,從而在Windows迫使它們屈服之前延遲您的計時器。
如果要獲得比Timer更高的准確性,請使用System.Diagnostics.Stopwatch
(在另一個線程中報告),它的時間可能為0.3毫秒,並且已與.NET環境集成。
另一種選擇是使用多媒體時間(精確到1毫秒左右)。
無論哪種方式, 這里都是關於此問題的出色教程。
分解:
計時器漂移通常會增加計時器延遲。 但是您看到相反的情況發生。 由於計時器不具有毫秒精度(它們僅在15ms范圍內准確),因此通常會以這種粒度觸發。 因此,實際上在某些情況下會在分鍾標記之前幾毫秒觸發計時器(導致其后立即觸發)。 如果您只要求它在新的分鍾內觸發,我將在幾毫秒的等待時間中進行補償(應該執行5毫秒)。
您的家用PC並沒有那么快(這意味着它在處理計時器處理程序時會出現額外的計時器漂移),並且通常在下一秒內觸發該事件。 您的工作PC有時會設法足夠快地處理計時器事件,使其記錄過去59秒的時間(我認為該時間已被截斷,可能在59.900〜59.999之間)。 如果計算機是多核的,則也可能發生這種情況,因為沒有線程生成延遲,並且計時器可以很快啟動。
這是造成計時器不正常的原因。
((60-現在。秒)* 1000-現在。毫秒)
這意味着如果現在。秒恰好是59,那么您的時間將在不到一秒鍾的時間內再次觸發。 這就是導致您的結果怪異的原因(計時器未按精確的0秒偏移觸發)。
計時器每秒觸發一次,將先前的日期/時間值保留在單獨的變量中,以及在第二部分更改時更新屏幕上的計時器,可能對您更有效率。
無需使用DateTime.Now並拉動各個部分,而只需使用Ticks即可 。 開始時獲取報價,然后計算下一個計時器報價的報價。 一旦發生計時器滴答聲,請使用最后一個值來計算下一個值。
范例 :
private const long MILLISECOND_IN_MINUTE = 60 * 1000;
private const long TICKS_IN_MILLISECOND = 10000;
private const long TICKS_IN_MINUTE = MILLISECOND_IN_MINUTE * TICKS_IN_MILLISECOND;
private System.Timers.Timer timer;
private long nextIntervalTick;
public void frmMain()
{
timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = GetInitialInterval();
timer.Start();
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString("hh:mm:ss tt"));
timer.Interval = GetInterval();
timer.Start();
}
private double GetInitialInterval()
{
DateTime now = DateTime.Now;
double timeToNextMin = ((60 - now.Second) * 1000 - now.Millisecond) + 15;
nextIntervalTick = now.Ticks + ((long)timeToNextMin * TICKS_IN_MILLISECOND);
return timeToNextMin;
}
private double GetInterval()
{
nextIntervalTick += TICKS_IN_MINUTE;
return TicksToMs(nextIntervalTick - DateTime.Now.Ticks);
}
private double TicksToMs(long ticks)
{
return (double)(ticks / TICKS_IN_MILLISECOND);
}
您可以像以前一樣使用秒和毫秒來執行此操作。 訣竅是要有一個起點來計算(而不是確定到下一分鍾多少秒)。 如果還有其他問題未在原始問題中提及,例如timer_Elapsed中的代碼可能需要更長的時間才能運行一分鍾,那么您將需要添加代碼來處理此問題。
如果您需要其他幫助,請發表評論。 否則,請選擇正確的答案。
另一個示例是使用System.Windows.Threading中的Timer。
using System;
using System.Windows.Threading;
namespace Yournamespace
{
public partial class TestTimer
{
DispatcherTimer dispatcherTimer1m;
public TestTimer()
{
dispatcherTimer1m = new DispatcherTimer();
dispatcherTimer1m.Tick += new EventHandler(DispatcherTimer1m_Tick);
dispatcherTimer1m.Interval = TaskHelper.GetSyncIntervalms;
dispatcherTimerm.Start();
}
private void DispatcherTimer1m_Tick(object sender, EventArgs e)
{
try
{
dispatcherTimer1m.Stop();
//Do your effort here
}
catch (Exception exc)
{
//Your exception handled here
}
finally
{
dispatcherTimer1m.Interval = TaskHelper.GetSyncInterval1m;
dispatcherTimer1m.Start();
}
}
}
public class TaskHelper
{
private const ushort internalUpdate = 15;//ms
public static TimeSpan GetSyncInterval1m => new TimeSpan(0, 0, 0, 60,internalUpdate).Subtract( new TimeSpan(0, 0, 0, DateTime.Now.Second, 0));
}
}
請記住,默認情況下,Windows Server設置為比客戶端版本更願意與后台任務共享資源,因此,如果服務器正在運行許多后台任務,則計時器准確性可能會受到影響。
您可以嘗試臨時更改它,以優先處理前台任務,以查看是否給出不同的結果-該設置位於“系統”控制面板中的某個位置,您需要查找兩個單選按鈕,一個顯示“程序”,另一個顯示“背景”服務”或類似內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.