有可能得到定时器.在处理事件后勾选事件

本文关键字:事件 处理事件 定时器 有可能 | 更新日期: 2023-09-27 18:11:44

我有一个关于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 
        }
    }
}

有可能得到定时器.在处理事件后勾选事件

了解计时器的工作原理可以让你感觉更好。它们由操作系统实现,底层winapi调用SetTimer()来启动计时器。然后,每当计时器滴答时,操作系统就会发出通知,您将获得一个WM_TIMER消息。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()方法。别这样。

Windows窗体定时器是单线程的,所以不可能在处理它时,你是在timer_Tick.

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

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);都是不必要的