定时器使窗体在运行后立即退出

本文关键字:退出 运行 窗体 定时器 | 更新日期: 2023-09-27 18:18:31

我试图在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次,它更新标签,但它退出没有警告或例外!

定时器使窗体在运行后立即退出

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

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

编辑

我刚刚在MSDN上快速浏览了一下,这是我发现的关于计时器线程的信息:

. windows . forms . 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的更新

使用System.Windows.Forms.Timer代替System.Threading.Timertimer在另一个线程中运行它触发的事件。但你不能从另一个线程改变UI因为你会得到跨线程操作异常。

如果你想要最小的更改,你可以添加到你的代码:

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]);
            }));
}