System.Threading.Timer没有启动?

8
我使用Compact Framework 2,SP2的C#语言。
设备的操作系统设置为随我的应用程序启动,我们称该应用程序为"Loader.exe"。
Loader只是一个简单的窗体,可以显示加载过程中的状态消息(如果需要),即“出现错误和异常消息”或“启动应用程序[xyz]”,同时在全屏窗体上运行基本的状态机。
因此,Loader窗体的构造函数在末尾添加以下内容:
try
{
    label1.Text = "Starting GUI Init Thread...";  //debug only message
    System.Threading.Timer guiInit = new System.Threading.Timer(
        RunStateMachine, null, 2000, System.Threading.Timeout.Infinite
        );
    //callback: RunStateMachine, null argument
    //initial callback is 2000ms from this point, and doesn't run again.
}
catch (Exception ex1)
{
    label1.Text = "GUI Init Error 2";
    Failure_Label.Text = ex1.Message;
}

"RunStateMachine"在与UI不同的线程上运行,允许表单显示,并且每当RunStateMachine需要与表单交互时(例如更新消息),我调用一个函数,该函数使用if(this.InvokeRequired){this.Invoke(...);} else{...}。所以我的问题是什么呢?有时候程序会挂起,这是因为定时器没有触发回调函数。我在上面的try块中添加了调试消息,还有很多其他地方告诉我它停在了哪里,包括在“RunStateMachine”的开始处添加的消息。最终,我的程序挂在了“Starting GUI Init Thread…”消息上。这告诉我线程计时器没有在我需要的时间运行。我的理论是,在计时器触发回调之前,它被垃圾回收了。这意味着如果计时器是全局的,并且在我到达RunStateMachine时显式处理,那么它将完美运行…但我不想认为我解决了这个问题,只是发现它在一个月后偶尔出现。你有什么想法吗?"

“挂在消息上”是什么意思?它似乎不是任何东西都可能被卡住的语句。 - H H
不要使用标签进行调试。寻找System.Diagnostics.Debug.Print()并收集更多信息。 - H H
@Henk Holterman,术语不正确。 "程序停止是因为它没有运行其他任何东西,而不是因为有任何阻塞。" 我觉得“挂起”会更简洁,但我知道它有不同的内涵。至于使用Debug.Print而不是标签:总的来说,我同意,但由于在此之前我不知道应用程序停止的位置或者是否可以访问停止后打印的内容,所以我需要一些更新屏幕的东西。谢谢你的提示。 - Peter Lacerenza
1个回答

10

我的理论是在定时器触发回调之前它被垃圾回收了。这意味着如果定时器是全局的,并且当我到达RunStateMachine时显式地将其处理掉,那么它将运行得完美...但我不想只是认为我解决了它,然后在一个月后出现这个问题。

看起来你想确认这是你的问题。 是的,这就是问题所在。

定时器存储在一个从未再次使用的局部变量中。这使得它有资格进行垃圾回收。定时器的垃圾回收导致定时器被禁用。

我建议将定时器存储在您表单类的实例字段中,并在回调被触发后从其中删除。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接