[英]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.