我试图在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次并更新标签,但它会退出而没有任何警告或异常!
答案 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,这就是我发现的定时器线程:
Timer用于以用户定义的间隔引发事件。此Windows计时器专为使用UI线程执行处理的单线程环境而设计。它要求用户代码具有可用的UI消息泵,并始终在同一个线程中运行,或者将调用编组到另一个线程上。
使用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]);
}));
}