簡體   English   中英

與系統時鍾同步運行每一分鍾(在Windows Server 2003上不起作用)

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM