在浸泡后可以获得Timer.Tick事件

时间:2015-09-08 08:38:57

标签: c# .net timer

我对System.Windows.Forms.Timer有疑问。处置后是否有可能获得Tick事件?例如,如果消息在消息循环中,我同时处理定时器。如果可能的话,防止它的最佳方法是什么。你现在有什么好的消息来源解释它,因为我找不到任何解释它的东西。以下是解释我的问题的相同代码:

namespace TestTimer
{
    public partial class Form1 : Form
    {
        ObjectWithTimer obj = null;
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if(obj != null) 
            {
                obj.Deinit();
                obj = null;
            }
            obj = new ObjectWithTimer();
        }
    }

    public class ObjectWithTimer
    {
        public Object o = new object();
        public Timer timer = new Timer();
        bool disposed = false;

        public ObjectWithTimer()
        {
            timer.Interval = 10;
            timer.Tick += new EventHandler(timer_Tick);
            timer.Enabled = true;
        }

        public void Deinit()
        {
            timer.Enabled = false;
            timer.Tick -= new EventHandler(timer_Tick);
            timer.Dispose();
            timer = null;
            disposed = true;
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            if (disposed)
            {
                //Is it possible to get here
                if (timer.Enabled) return;
            }
            //doing something 
        }
    }
}

3 个答案:

答案 0 :(得分:4)

了解计时器如何工作可以帮助您更好地理解它。它们由操作系统实现,启动计时器的基础winapi调用是SetTimer()。然后操作系统会在计时器滴答时发布通知,您将获得WM_TIMER message。 Winforms中的管道确保在收到此消息时运行Tick事件处理程序。

这些消息存储在消息队列中,这是一个与窗口关联的内部数据结构。此队列序列化消息,它是一种基本机制,可确保您永远不会丢失鼠标单击或键盘按键,即使窗口没有响应,因为UI线程正忙于其他内容。

此队列有理由谨慎,当您处理计时器时队列存储WM_TIMER消息时会发生什么?除非做了一些激烈的事情,否则你仍然会收到该消息,并且你的Tick事件处理程序将会触发。

但无需担心,WM_TIMER属于以特殊方式生成的一小组消息。它们是合成消息,只有在程序要求使用GetMessage()消息时才会生成它。属于该组的其他常见消息是WM_PAINT,它会触发Paint事件。请注意如何随时调用Invalidate(),您仍然只能获得一个Paint事件。 WM_MOUSEMOVE是另一个,它会触发MouseMove事件。无论您移动鼠标的速度有多快,您都可以推理出一些事情,您永远不会使用鼠标移动消息来填充消息队列。

这些合成消息的另一个特征是它们似乎具有“低优先级”。鉴于它们仅在消息队列为空时才被合成。这就是键盘消息和鼠标点击总是在绘画之前生成事件的原因。

简而言之,如果您要求提供消息并且计时器仍然存在,则只能获取WM_TIMER消息。 Timer.Dispose()方法调用KillTimer()。这结束了任何仍然获得Tick事件的机会。只有可能被搞砸的方法是从工作线程调用Stop()或Dispose()方法。不要那样做。

答案 1 :(得分:2)

Windows窗体计时器是单线程的,因此在处理它时你不可能在timer_Tick中。

此外,您不会以deinit功能分离您的活动。

答案 2 :(得分:1)

这很容易测试。我稍微修改了你的代码:

public class Form1 : Form
{
    public Form1()
    {
        var button = new Button();
        button.Click += button1_Click;

        Controls.Add(button);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var obj = new ObjectWithTimer();

        Thread.Sleep(2000);

        obj.Deinit();
    }
}

public class ObjectWithTimer
{
    public System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
    bool disposed = false;

    public ObjectWithTimer()
    {
      timer.Interval = 100;
      timer.Tick += new EventHandler(timer_Tick);
      timer.Enabled = true;
    }

    public void Deinit()
    {
      timer.Enabled = false;
      timer.Tick -= new EventHandler(timer_Tick);
      timer.Dispose();
      timer = null;
      disposed = true;
    }

    private void timer_Tick(object sender, EventArgs e)
    {
      "Ticked".Dump();
    }
}

Thread.Sleep确保在计时器执行滴答时占用UI线程。

结果呢?不,禁用定时器后Tick不会触发。即使是timer.Tick -= new EventHandler(timer_Tick);也是不必要的。