计时器导致表单在运行后立即退出

时间:2011-12-22 13:27:03

标签: c# winforms multithreading timer

我试图在MDI表单的状态栏中显示CPU和内存负载。如果我只是用方法调用它们,cpu和内存状态是正确的。但是现在我想制作一个Timer,只要应用程序正在运行,它就会继续更新两个标签:

public System.Threading.Timer MainTimer;
public System.Threading.TimerCallback MainTimerCallback;

private void InitializeTimer()
{
    MainTimerCallback = new System.Threading.TimerCallback(MainTimer_Tick);
    MainTimer = new System.Threading.Timer(MainTimerCallback,this,0,100);
}

private void MainTimer_Tick(object obj)
{
    UpdateSystemDiagnostics();
}

然后我在MDI表单构造函数中对此进行编码:

public MainForm()
{
    InitializeComponent();

    InitializeSystemDiagnostics();
    InitializeTimer();
}

这是我的诊断代码:

private PerformanceCounter _cpuLoad;
private PerformanceCounter _ramFree;
public float[] SystemDiagnostic = new float[2] { 0, 0 };

private void InitializeSystemDiagnostics()
{
    //Diagnostics
    _cpuLoad = new PerformanceCounter { CategoryName = "Processor", CounterName = "% Processor Time", InstanceName = "_Total" };
    _ramFree = new PerformanceCounter("Memory", "Available MBytes");
}

private void UpdateSystemDiagnostics()
{
    SystemDiagnostic[0] = _cpuLoad.NextValue();
    SystemDiagnostic[1] = _ramFree.NextValue();

    _labelCpuStatus.Text = string.Format("CPU LOAD: ") + string.Format("{0:0.##}%", SystemDiagnostic[0]).PadRight(8);
    _labelMemoryStatus.Text = string.Format("FREE MEMORY: {0}MB", SystemDiagnostic[1]);
}

为什么这会导致表单一打开就关闭?即使我点击调试,它也会立即打开和关闭!

另外我的另一个问题是:这个计时器是否在另一个线程上运行?如果我将一个耗时的操作传递给它的Tick事件,是否会导致UI冻结或断断续续?

更新

当我在这一行上设置断点时:

_labelCpuStatus.Text = string.Format("CPU LOAD: ") + string.Format("{0:0.##}%", SystemDiagnostic[0]).PadRight(8);

我注意到它可以工作2到3次并更新标签,但它会退出而没有任何警告或异常!

3 个答案:

答案 0 :(得分:3)

使用System.Windows.Forms.Timer而不是System.Threading.Timer System.Threading.Timer在另一个线程中运行它的触发事件。但是,您无法从另一个线程更改UI,因为您将获得跨线程操作异常。

答案 1 :(得分:3)

如果表单只是立即打开和关闭,则很可能在构造函数代码中有一个未处理的异常。 WinForms有一种令人烦恼的习惯,就是吞下这样的异常。如果你围绕它谷歌,你应该找到更多的信息。您可以通过转到Debug > Exceptions并选中下的公共语言运行时例外旁边的框来查看此例外的内容(如果我认为这是正确的) >抛出该异常即可。这应该打破该类别下的所有例外。

至于计时器是否在另一个线程上运行,检查的最佳方法是在Thread.Sleep(...some large number...)事件的处理程序中添加Tick(当表单运行时)并查看它是否冻结表格。如果是这样,它就在同一个线程上运行。

修改

我刚看了MSDN,这就是我发现的定时器线程:

System.Windows.Forms.Timer:

  

Timer用于以用户定义的间隔引发事件。此Windows计时器专为使用UI线程执行处理的单线程环境而设计。它要求用户代码具有可用的UI消息泵,并始终在同一个线程中运行,或者将调用编组到另一个线程上。

System.Threading.Thread.Timer

  

使用TimerCallback委托指定希望Timer执行的方法。定时器委托是在构造定时器时指定的,不能更改。该方法不会在创建计时器的线程上执行;它在系统提供的ThreadPool线程上执行。

所以,如果我的理解是正确的; System.Windows.Forms.Timer在UI线程上执行(因此如果对其执行长操作将冻结UI)并且System.Threading.Thread.Timer在与UI的不同线程上执行(因此如果执行长时不应冻结UI对它的操作)。

注意:只是为了澄清;我不是说UI应该在另一个线程上更新 - 相反,我只是想说明已经注意到的定时器之间的差异。 UI只能在UI线程上更新。如果需要执行长操作而不冻结UI线程(独立于任何UI元素),则可以在单独的线程上执行它们,然后可以在UI线程上调用UI的更新。 < / p>

答案 2 :(得分:1)

如果您想要最少的更改,可以将其添加到您的代码中:

private void UpdateSystemDiagnostics()
{
    SystemDiagnostic[0] = _cpuLoad.NextValue();
    SystemDiagnostic[1] = _ramFree.NextValue();
 this.Invoke(new MethodInvoker(delegate
            {
                _labelCpuStatus.Text = string.Format("CPU LOAD: ") + string.Format("{0:0.##}%", SystemDiagnostic[0]).PadRight(8);
    _labelMemoryStatus.Text = string.Format("FREE MEMORY: {0}MB", SystemDiagnostic[1]);
            }));

}