【问题标题】:Intensive Stop/Start of a C# TimerC# 计时器的密集停止/启动
【发布时间】:2012-02-09 18:42:18
【问题描述】:

我创建了一个看门狗定时器(使用 System.Windows.Forms.Timer),如果长时间没有收到小数据包,就会触发:

using System.Windows.Forms;
public class Watchdog
{
    private Timer Timer;

    public void Go()
    {
        Timer.Start();
    }

    public void Reset()
    {
        Timer.Stop();
        Timer.Start();
    }

    private void OnTimerExpired(object State)
    {
        Timer.Stop();
        DoSomething();
    }

    public Watchdog()
    {
        Timer = new Timer();
        Timer.Tick += new EventHandler(OnTimerExpired);
        Timer.Interval = (1000 * Timeout);            
    }
}

主代码调用Go(),然后每收到一个数据包就调用Reset()。如果计时器到期,则调用OnTimerExpired()

由于每秒可能接收数百个数据包,并且由于应用程序的主要工作是响应这些数据包,我开始怀疑重置计时器是否会占用大量 CPU/OS。

知道以这种方式调用Timer.Stop()/Timer.Start() 会如何影响性能(就延迟而言)?

【问题讨论】:

  • 使用System.Debug.Stopwatch 类会更好吗?

标签: c# performance timer


【解决方案1】:

使用简单的时间跨度或整数变量作为标志。当计时器滴答作响时,它会检查 Stopwatch 对象以查看自上次更新标志以来已经过去了多少时间。如果它长于您的超时值,您将触发您的看门狗代码。

现在,当有新数据包进来时,其他代码可以使用秒表更新您的时间跨度标志值,而不是重置您的计时器。

您还应该将计时器的滴答间隔设置为您希望实际超时持续时间的 1/2 左右,或者在事件中设置代码来设置您的时间间隔,这样您的下一个滴答事件就在您之后几毫秒如果现在断开连接会超时。否则,在最后一个数据包在滴答事件之后很快到达的情况下,您最终等待的时间几乎是超时时间的两倍。

【讨论】:

  • 不要为此使用DateTime!在夏令时转换期间以及用户更改时钟时,您将遇到问题。您最好在程序启动时启动Stopwatch,并在每次收到消息时保存其Elapsed 值。然后计时器滴答可以将当前Elapsed 值与最后收到的消息值进行比较。跨度>
【解决方案2】:

顺便说一句,另一种选择是只设置一个Boolean 标志,只要有消息进入,就会设置该标志。计时器事件处理程序会检查该标志并在未设置时发出警报。所以你有:

private bool GotAMessage = false;

void MessageReceived()
{
    // happens whenever a message is received
    GotAMessage = true;
}

void OnTimerExpired(object state)
{
    if (!GotAMessage)
    {
        // didn't receive a message in time.
    }
    GotAMessage = false;
}

【讨论】:

    【解决方案3】:

    一个更简单的选择是调用 WatchDog 类上的一个方法,该方法在接收到数据包时更新公共 lastPacketReceived 值。然后你只需要在 WatchDog 类中启动一个定时器一次,每个超时间隔计时一次,并将当前时间与 lastPacketReceived 值进行比较:

    public static class WatchDog
    {
        static object locker = new object();
        static long lastPacketReceived;
        static Stopwatch stopWatch = new Stopwatch();
        static long threshold = 5000;
        static WatchDog()
        {
            Timer watchDogTimer = new Timer(1000);
            watchDogTimer.Elapsed += new ElapsedEventHandler(watchDogTimer_Elapsed);
            watchDogTimer.Start();
            stopWatch.Start();
        }
    
        static void watchDogTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            lock (locker)
            {
                if ((stopWatch.ElapsedMilliseconds - lastPacketReceived) > threshold)
                {
                    // threshold exceeded
                }
            }
        }
    
        public static void PacketReceived()
        {
            lock (locker)
            {
                lastPacketReceived = stopWatch.ElapsedMilliseconds;
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      知道如何调用Timer.Stop()/Timer.Start()

      这种方式可能会影响性能(就延迟而言)?

      不太可能衡量执行此操作所需的资源量。除非您遇到性能问题,否则不要试图解决性能问题,至少使用软件来分析软件以查看其是否存在实际问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-01-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多