我在 CF 项目(Windows Embedded CE 6.0)、VS2005 C#、.NET 2.0 中使用 System.Threading.Timer。 这个计时器是需要的,因为像这样使用时不可能重入:
private System.Threading.Timer mainTimer;
private void MainForm_Load(object sender, EventArgs e)
{
// other initializations
mainTimer = new System.Threading.Timer(new TimerCallback(timerMain_Tick),
null, 100, Timeout.Infinite);
}
也就是说,使用了 dueTime 参数,但没有使用 period 。只要 period 为 Timeout.Infinite,计时器就会只触发一次。通过检查表单的 InvokeRequired 属性,使计时器成为线程安全的。请注意检查是否为空。这与我的问题有关,我很快就会回答这个问题。
private void timerMain_Tick(object stateInfo)
{
if (mainTimer != null)
{
if (this.InvokeRequired)
{
this.Invoke((ThreadStart)delegate
{
TimerProcess();
});
}
else
{
TimerProcess();
}
}
}
计时器在退出之前必须自行重新启动。
private void TimerProcess()
{
try
{
// do work here
}
finally
{
// retrigger
mainTimer.Change(mainTimerInterval, Timeout.Infinite);
}
}
我遇到的问题是优雅地阻止这该死的事情。
private void MainForm_Closing(object sender, CancelEventArgs e)
{
// shut down timer
mainTimer.Change(Timeout.Infinite, Timeout.Infinite);
mainTimer.Dispose();
mainTimer = null;
}
大约 10 次中有 3 次,计时器无论如何都会触发,并且我收到“对象处置”错误。计时器代码尝试在检查 null 后调用计时器方法。 我怀疑计时器触发了,并且在表单关闭时其线程被挂起。我尝试了状态机枚举:
正常状态运行
Form_Closing 设置 Stopping 状态并在 Thread.Sleep() 循环中等待 Stopped 状态
定时器看到停止并设置停止状态(而不是重新触发自身)
我遇到的问题是计时器线程不会抢占表单关闭方法,因此陷入无限循环。 如何解决这个问题?请注意,在 CF 中,没有 Dispose(WaitHandle) 方法。
有趣的问题。 Compact Framework 中的 Timer 似乎没有太多选项。
我不确定您的特定代码是如何工作的,因此添加单个静态布尔值可能会也可能不会解决您的问题。
以下是我如何更改代码以接受
timerOK
值。如果这不能解决您的问题,它可以为您提供如何解决此问题的想法。
private static bool timerOK;
private static long mainTimerInterval = 200;
private System.Threading.Timer mainTimer;
private void MainForm_Load(object sender, EventArgs e) {
timerOK = true;
mainTimer = new System.Threading.Timer(new TimerCallback(timerMain_Tick), null, 100, Timeout.Infinite);
}
private void MainForm_Closing(object sender, CancelEventArgs e) {
timerOK = false;
mainTimer.Change(Timeout.Infinite, Timeout.Infinite);
mainTimer.Dispose();
mainTimer = null;
}
private void timerMain_Tick(object stateInfo) {
if (timerOK && (mainTimer != null)) {
if (this.InvokeRequired) {
this.Invoke((ThreadStart)delegate {
TimerProcess();
});
} else {
TimerProcess();
}
}
}
private void TimerProcess() {
if (!timerOK) return;
try {
// do work here
} finally {
// retrigger
mainTimer.Change(mainTimerInterval, Timeout.Infinite);
}
}
我正在使用 MVP,所以我的布局有点不同,但本质上我有两个相同的问题需要修复:
第一个很容易修复,如上面的 pwrgreg007 所示,只需在“关闭”过程中的某个时间(在处理表单目标之前)关闭并清空计时器,然后在计时器处理事件开始时进行空检查。
第二个问题有点棘手,即使计时器(和您的表单)在处理循环开始时运行,也无法阻止它在进程中途关闭,因为它在不同的线程上运行。为了防止这种情况,我创建了一个在计时器执行和计时器关闭期间使用的锁。
//simplified presenter
public class Presenter
{
private const int REFRESH_TIME_MILLISECONDS = 5000;
private view _view;
private Timer _timer;
private object _timerLock = new object();
//CTOR
public Presenter()
{
_view = new View();
Startup();
}
//spin up presenter
public void Startup(){
//bind view shutdown event
_view.ViewClosing += Shutdown;
//start timer
_timer = new Timer(DoTimerStuff, null, REFRESH_TIME_MILLISECONDS, Timeout.Infinite);
}
//spin down presenter
public void Shutdown()
{
//wait for any DoTimerStuff locks to expire
lock (_timerLock)
{
//stop the timer
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_timer.Dispose();
_timer = null;
}
//close the view
_view.Shutdown();
}
//timer tick
private void DoTimerStuff(object state)
{
//grab a lock so we can ensure the timer doesn't get shutdown mid way through
lock (_timerLock)
{
//make sure the timer isn't shutdown (from form closing)
if (_timer == null) return;
//do your stuff here
_view.SomeInvokedCheckedProperty = "SomeValue";
//etc...
//schedule next timer execute (runs every runtime + refresh time)
_timer.Change(REFRESH_TIME_MILLISECONDS, Timeout.Infinite);
}
}
}
//simplified view
public class View : Form
{
//view properties (make sure they are invoke checked)
public SomeInvokedCheckedProperty {get;set;}
//Bound to ViewClosing
private void View_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
//stop the view closing itself
e.Cancel = true;
//tell the presenter to handle closing instead
if (ViewClosing != null) ViewClosing.Invoke();
}
}
这样..
DoTimerStuff()
已锁定并且当前正在运行,则计时器将等待关闭(保持表单关闭)DoTimerStuff()
将等待,并且当它继续时,它将正确地看到计时器已关闭(并且不执行任何操作)。我刚才也遇到了同样的问题,今天看到了你的帖子。 尽管它已有 10 多年的历史,我认为它仍然可能对任何人来说都很有趣,所以这是我的答案。我相信你的结论是正确的,即项目结束时还有一些未完成的事情。对我来说解决这个问题的是添加Application.DoEvents。因此,如果您按如下方式调整代码,应该没问题:
// shut down timer
mainTimer.Dispose();
Application.DoEvents();
Dispose();
Application.DoEvents 在关闭前结束未完成的事情。就是这样。
顺便说一句,如果你把它放在 Form_Closing 方法中(由主程序中的 Close() 命令触发),你当然可以省略 Dispose() 命令。 希望有帮助。