【问题标题】:Timer causes the form to exit immediately after running计时器导致表单在运行后立即退出
【发布时间】:2011-12-22 13:27:03
【问题描述】:

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

【问题讨论】:

  • 您是如何运行该表单的?即Program.csMain(...)?
  • 就像一个普通的winform...是的,它通过Program.cs运行

标签: c# winforms multithreading timer


【解决方案1】:

如果表单只是立即打开和关闭,则很可能您的构造函数代码中有未处理的异常。 WinForms 有一个讨厌的习惯,就是吞下这样的异常。如果你用谷歌搜索它,你应该会找到更多关于它的信息。您可以通过转到 Debug > Exceptions 并选中 Thrown 下的 Common Language Runtime Exceptions 旁边的框来查看此异常的内容(如果我认为这是原因的话) 。这应该打破该类别下的所有例外情况。

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

编辑

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

System.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 的更新。

【讨论】:

  • 您的最后一点是正确的,但 OP 专门谈到了更新 UI。您会想要从 UI 线程中执行此操作。
  • 是的,你知道。我只是想说明与计时器的区别。但是,您可以在单独的线程上执行长操作(独立于任何 UI 元素),并回调 UI 线程以更新 UI。
  • 是的,有很多变种:)
  • @codesparkle 谢谢。我很高兴我设法澄清了我试图解释的内容。
  • 所以我还没有得到我必须做的事情:D
【解决方案2】:

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

【讨论】:

  • 但是听说有人使用了UI线程,导致UI无响应。我错了吗?
  • 没有其他方法可以更改 UI。仅来自 UI 线程。唯一不应该做的就是在 ui 线程中进行长时间的计算。标签文本更新没问题,正是需要在 UI 线程中完成的事情
  • @Sean87 Forms.Timer不会导致 UI 在刻度之间无响应。当它滴答作响并调用它的方法时——它会的。但是,如果该方法很快,则应该没有问题。但最好的是 - 尝试使用 Forms.Timer 的简单程序。
【解决方案3】:

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

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

}

【讨论】:

  • 感谢这增加了我的知识!
猜你喜欢
  • 2022-06-23
  • 1970-01-01
  • 2018-01-24
  • 1970-01-01
  • 2017-07-02
  • 2015-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多