定时器使窗体在运行后立即退出
本文关键字:退出 运行 窗体 定时器 | 更新日期: 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]);
}));
}