8

我将 C# 与 Compact Framework 2 SP2 一起使用。

设备的操作系统设置为使用我的应用程序启动,我们将应用程序称为“Loader.exe”。

加载器就是这样:一个单一的、简单的表单,在整个加载过程中显示状态消息,如有必要(外行术语,即存在错误和异常消息,或“正在启动应用程序 [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(...);} 其他{...}

那么,我的问题?
间歇性地,我的程序会挂起,这是因为计时器没有触发回调。我在上面的 try 块中添加了调试消息,以及许多其他地方告诉我它挂断的位置,包括“RunStateMachine”最开始处的一条消息。最终,我的程序挂在“正在启动 GUI 初始化线程...”的消息上。

这告诉我线程计时器没有在我需要的时候运行。
我的理论是它在定时器触发回调之前被垃圾收集。这意味着如果计时器是全局的,然后在我到达 RunStateMachine 时显式处理,它将完美运行......但我不想认为我解决了它只是为了发现这个问题会在一个月后间歇性地出现。

想法?

4

1 回答 1

10

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

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

计时器存储在一个永远不会再次使用的局部变量中。这使它有资格获得 GC。Timer 的 GC 会导致最终确定,从而导致计时器被禁用。

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

于 2012-10-03T15:55:09.717 回答