簡體   English   中英

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

[英]Is it possible to get Timer.Tick event after diposing it

我有一個關於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中。

此外,您不會在deinit功能中分離您的事件。

這很容易測試。 我已經修改了你的代碼:

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); 沒必要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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