0

提前为相当长的帖子和大量代码道歉。

我的应用程序具有定时自动保存功能。用户要求我提供剩余时间的视觉指示器。我对倒数计时器进行了一些研究,并最终编写了以下课程:

public class CountDownTimer
    {

        private Timer timer;
        private int remaining;

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Count down ticked delegate. </summary>
        ///
        /// <remarks>   Jon, 18/06/2012. </remarks>
        ///
        /// <param name="remaining">    The remaining. </param>
        /// <param name="maximum">      The maximum. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public delegate void CountDownTickedDelegate(int remaining, int maximum);

        /// <summary>   Event queue for all listeners interested in Ticked event. </summary>
        public event CountDownTickedDelegate Ticked;

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Count down percent delegate. </summary>
        ///
        /// <remarks>   Jon, 18/06/2012. </remarks>
        ///
        /// <param name="percent">  The percent. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public delegate void CountDownPercentDelegate(int percent);

        /// <summary>   Event queue for all listeners interested in Percent events. </summary>
        public event CountDownPercentDelegate Percent;

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Count down done delegate. </summary>
        ///
        /// <remarks>   Jon, 18/06/2012. </remarks>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public delegate void CountDownDoneDelegate();

        /// <summary>   Event queue for all listeners interested in Done events. </summary>
        public event CountDownDoneDelegate Done;

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Gets or sets the maximum value to count down from </summary>
        ///
        /// <value> The maximum value. </value>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public int Maximum
        {
            get;
            set;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Gets or sets a value indicating whether the timer is Paused. </summary>
        ///
        /// <value> true if paused, false if not. </value>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public bool Paused
        {
            get;
            set;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Starts this CountDownTimer. </summary>
        ///
        /// <remarks>   Jon, 18/06/2012. </remarks>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public void Start()
        {
            timer = new Timer {
                Interval = 1000
            };
            timer.Tick += onTimerTick;
            timer.Enabled = true;
            remaining = Maximum;
            Paused = false;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Stops this CountDownTimer. </summary>
        ///
        /// <remarks>   Jon, 18/06/2012. </remarks>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public void Stop()
        {
            if (timer == null)
            {
                return;
            }

            Paused = true;
            timer.Enabled = false;
            timer = null;
            if (Percent != null)
            {
                Percent(100);
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Resets and restarts this CountDownTimer. </summary>
        ///
        /// <remarks>   Jon, 18/06/2012. </remarks>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

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

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Handles the timer tick event. </summary>
        ///
        /// <remarks>   Jon, 18/06/2012. </remarks>
        ///
        /// <param name="sender">   Source of the event. </param>
        /// <param name="e">        Event information to send to registered event handlers. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        private void onTimerTick(object sender, EventArgs e)
        {
            if (remaining > 0)
            {
                if (Ticked != null)
                {
                    Ticked(remaining, Maximum);
                }

                if (Percent != null)
                {
                    int percent = remaining * 100 / Maximum;
                    Percent(percent);
                }

                if (!Paused)
                {
                    remaining--;
                }
                else
                {
                    remaining = Maximum;
                }
            }
            else
            {
                Stop();

                if (Done != null)
                {
                    Done();
                }
            }
        }
    }

我正在使用计时器,每次它“触发”时,我都会减少一个计数器。每次递减都会启动一个事件,以便我的表单可以直观地呈现它。当计数器达到零时,另一个事件将启动自动保存。

如果用户手动保存或打开新项目,还包括一些其他位以允许重新启动自动保存。

它似乎对我有用。但是,用户报告说计时器运行的时间越长,自动保存之间的间隔就越短。我将计时器设置为每秒滴答一次,我的调查表明它以两倍的速度运行。因此,如果计数器设置为 60(秒),那么它每 30 次运行一次。我无法复制用户看到的行为,但他的日志肯定显示事情运行得太快了。

这与主应用程序在同一个线程中 - 这可能是一个问题。到目前为止,我所有的测试都没有出现任何问题,除了蜱虫似乎每三次左右连续触发两次。

非常感谢您的任何见解。

4

2 回答 2

1

我看到的一个问题是,如果CountDownTimer.Start()被调用两次(甚至多次),而没有适当的CountDownTimer.Stop()调用,您最终会得到Timer对象的两个或多个激活实例,它们都调用您的onTimerTick()事件处理程序。

这可能会导致您描述的效果,因为所有正在运行的Timer实例都会分别减少剩余的迭代。

你的调用代码可以吗?

编辑:

作为一种解决方法,我建议您Stop()Start(). 或者更好的是,您不会Timer为每个新的倒计时操作重新创建对象。在构造函数中创建Timer对象并仅操作其属性。

在处置对象时从实例中删除onTimerTick()事件处理程序也不是一个坏主意。否则 GC 无法收集实例,因为它仍然持有对其实例的引用。timerTimertimerCountDownTimer

于 2012-07-11T11:37:43.293 回答
0

Timer 的 Elapsed 事件在每个间隔后不断触发。您需要先停止 Elapsed 事件处理程序方法中的计时器,然后在离开方法时再次启动它。否则,如果您在事件处理程序方法中的代码需要大约 1 秒,则 Elapsed 事件将再次引发。例如

private void onTimerTick(object sender, EventArgs e)
{
    try
    {
        timer.Stop();
        //Do your stuff here
    }
    finally { timer.Start(); }
}
于 2012-07-11T11:13:59.910 回答