如果表单应用程序关闭 C#,如何防止发生 Timer Elapsed 事件
How to prevent Timer Elapsed event from occurring if forms application closes C#
我在 windows 表单应用程序 (Visual C#) 中有一个计时器,当我想退出该应用程序时出现问题。
定时器被定义为表单的成员 class:
partial class Form1
{
//These are the members in question:
internal ComACRServerLib.Channel channel;
private System.Timers.Timer updateStuff;
}
计时器是 declared/constructed 在表单应用程序的构造函数中:
public Form1()
{
InitializeComponent();
updateStuff = new System.Timers.Timer();
updateStuff.Elapsed += new System.Timers.ElapsedEventHandler(updateStuff_Elapsed);
}
按下按钮即可启动和配置计时器:
private void btnAcquire_Click(object sender, EventArgs e)
{
updateStuff.Interval = 100;
updateStuff.Enabled = true;
updateStuff.AutoReset = true;
updateStuff.Start();
}
当计时器结束时,它调用 updateStuff_Elapsed
,它获取要用 setText
显示的信息(有一些代码可以确保对 setText
的调用是线程安全的) .
private void updateStuff_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!channel.isOffline)
{
object[] status = channel.GetACRCustom("P6144");
setText(System.Convert.ToString(status[0]));
}
}
public delegate void setTextDelegate(string text);
public void setText(string text)
{
if (this.lblTest.InvokeRequired == true)
{
setTextDelegate d = new setTextDelegate(setText);
this.Invoke(d, new object[] { text });
}
else
{
lblTest.Text = text;
}
}
在应用程序退出时,我尝试通过以下方式摆脱计时器并防止它再次触发:
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
updateStuff.AutoReset = false;
updateStuff.Stop();
updateStuff.Close();
updateStuff.Dispose();
components.Dispose();
}
base.Dispose(disposing);
}
但是如果计时器自动运行并且我退出程序,我总是会收到错误消息,即 Timer Elapsed 事件 updateStuff_elapsed
调用的例程正在尝试使用已经释放的资源!尽管我已尽力在处理发生之前停止并销毁计时器。
如何在应用程序关闭时阻止计时器触发?
编辑
我尝试移动 Dispose
代码以尝试强制关闭计时器,但没有成功。我还尝试使用 updateStuff.Elapsed -= updateStuff_Elapsed
在停止和处理之前删除事件调用;
protected override void Dispose(bool disposing)
{
//now this code HAS to run always.
updateStuff.Elapsed -= updateStuff_Elapsed;
updateStuff.AutoReset = false;
updateStuff.Stop();
updateStuff.Close();
updateStuff.Dispose();
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
处理处置事件为时已晚(在垃圾收集期间)。您需要对 FormClosing 事件执行清理过程。
如 System.Timers.Timer 的文档中所述,Timer 的事件处理程序调用在 ThreadPool 线程上排队。因此,您必须假设事件处理程序可以一次调用多次,或者可以在禁用 Timer 后调用。因此,事件处理程序的设计必须能够正确处理这些情况。
首先,将定时器的SynchronizingObject属性设置为Form的实例。这会将事件处理程序的所有调用编组到 UI 线程,因此我们不需要为锁定表单字段而烦恼(我们将始终从同一个 UI 线程访问所有内容)。使用此 属性 设置,您也不需要在 setText 方法中调用 this.Invoke(...)。
public Form1()
{
updateStuff = new System.Timers.Timer();
updateStuff.SynchronizingObject = this;
...
}
public void setText(string text)
{
lblTest.Text = text;
}
然后创建标志,让您知道定时器是否已被处理。然后只需在事件处理程序中检查此标志:
partial class Form1
{
private bool Disposed;
....
}
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
updateStuff.Dispose();
Disposed = true;
}
base.Dispose(disposing);
}
private void updateStuff_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if(!Disposed)
{
if (!channel.isOffline)
{
object[] status = channel.GetACRCustom("P6144");
setText(System.Convert.ToString(status[0]));
}
}
}
进一步阐述 Ron 和 Marc 所说的话,
解决方案是覆盖 class Form
的 OnFormClosing
方法,其中 Form1
是 subclassed:
protected override void OnFormClosing(FormClosingEventArgs e)
{
//It appears it needs everything here to be turned off...amazing.
updateStuff.Elapsed -= updateStuff_Elapsed;
updateStuff.AutoReset = false;
updateStuff.Stop();
updateStuff.Dispose();
base.OnFormClosing(e);
}
另一种解决方案是将计时器放在与用户界面相同的线程上,如 Nuf 所述。
我在 windows 表单应用程序 (Visual C#) 中有一个计时器,当我想退出该应用程序时出现问题。
定时器被定义为表单的成员 class:
partial class Form1
{
//These are the members in question:
internal ComACRServerLib.Channel channel;
private System.Timers.Timer updateStuff;
}
计时器是 declared/constructed 在表单应用程序的构造函数中:
public Form1()
{
InitializeComponent();
updateStuff = new System.Timers.Timer();
updateStuff.Elapsed += new System.Timers.ElapsedEventHandler(updateStuff_Elapsed);
}
按下按钮即可启动和配置计时器:
private void btnAcquire_Click(object sender, EventArgs e)
{
updateStuff.Interval = 100;
updateStuff.Enabled = true;
updateStuff.AutoReset = true;
updateStuff.Start();
}
当计时器结束时,它调用 updateStuff_Elapsed
,它获取要用 setText
显示的信息(有一些代码可以确保对 setText
的调用是线程安全的) .
private void updateStuff_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!channel.isOffline)
{
object[] status = channel.GetACRCustom("P6144");
setText(System.Convert.ToString(status[0]));
}
}
public delegate void setTextDelegate(string text);
public void setText(string text)
{
if (this.lblTest.InvokeRequired == true)
{
setTextDelegate d = new setTextDelegate(setText);
this.Invoke(d, new object[] { text });
}
else
{
lblTest.Text = text;
}
}
在应用程序退出时,我尝试通过以下方式摆脱计时器并防止它再次触发:
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
updateStuff.AutoReset = false;
updateStuff.Stop();
updateStuff.Close();
updateStuff.Dispose();
components.Dispose();
}
base.Dispose(disposing);
}
但是如果计时器自动运行并且我退出程序,我总是会收到错误消息,即 Timer Elapsed 事件 updateStuff_elapsed
调用的例程正在尝试使用已经释放的资源!尽管我已尽力在处理发生之前停止并销毁计时器。
如何在应用程序关闭时阻止计时器触发?
编辑
我尝试移动 Dispose
代码以尝试强制关闭计时器,但没有成功。我还尝试使用 updateStuff.Elapsed -= updateStuff_Elapsed
在停止和处理之前删除事件调用;
protected override void Dispose(bool disposing)
{
//now this code HAS to run always.
updateStuff.Elapsed -= updateStuff_Elapsed;
updateStuff.AutoReset = false;
updateStuff.Stop();
updateStuff.Close();
updateStuff.Dispose();
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
处理处置事件为时已晚(在垃圾收集期间)。您需要对 FormClosing 事件执行清理过程。
如 System.Timers.Timer 的文档中所述,Timer 的事件处理程序调用在 ThreadPool 线程上排队。因此,您必须假设事件处理程序可以一次调用多次,或者可以在禁用 Timer 后调用。因此,事件处理程序的设计必须能够正确处理这些情况。
首先,将定时器的SynchronizingObject属性设置为Form的实例。这会将事件处理程序的所有调用编组到 UI 线程,因此我们不需要为锁定表单字段而烦恼(我们将始终从同一个 UI 线程访问所有内容)。使用此 属性 设置,您也不需要在 setText 方法中调用 this.Invoke(...)。
public Form1()
{
updateStuff = new System.Timers.Timer();
updateStuff.SynchronizingObject = this;
...
}
public void setText(string text)
{
lblTest.Text = text;
}
然后创建标志,让您知道定时器是否已被处理。然后只需在事件处理程序中检查此标志:
partial class Form1
{
private bool Disposed;
....
}
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
updateStuff.Dispose();
Disposed = true;
}
base.Dispose(disposing);
}
private void updateStuff_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if(!Disposed)
{
if (!channel.isOffline)
{
object[] status = channel.GetACRCustom("P6144");
setText(System.Convert.ToString(status[0]));
}
}
}
进一步阐述 Ron 和 Marc 所说的话,
解决方案是覆盖 class Form
的 OnFormClosing
方法,其中 Form1
是 subclassed:
protected override void OnFormClosing(FormClosingEventArgs e)
{
//It appears it needs everything here to be turned off...amazing.
updateStuff.Elapsed -= updateStuff_Elapsed;
updateStuff.AutoReset = false;
updateStuff.Stop();
updateStuff.Dispose();
base.OnFormClosing(e);
}
另一种解决方案是将计时器放在与用户界面相同的线程上,如 Nuf 所述。