C# Windows窗体应用程序中的多线程调用?
我试图让我的C#应用程序多线程,因为有时候,我会遇到一个异常,说我以不安全的方式调用了一个线程。我以前从来没有在程序中做过任何多线程,所以如果我听起来对这个问题有点无知,请容忍我 我的程序概述是,我想做一个性能监控应用程序。这需要使用C#中的进程和性能计数器类来启动和监视应用程序的处理器时间,并将该数字发送回UI。但是,在实际调用性能计数器的nextValue方法(由于计时器设置为每秒执行一次)的方法中,我有时会遇到前面提到的异常,该异常会涉及以不安全的方式调用线程 我附上了一些代码供您阅读。我知道这是一个很费时的问题,所以如果有人能为我提供帮助,告诉我在哪里制作一个新线程以及如何安全地调用它,我将非常感激。我试着查看MSDN上的内容,但这让我有点困惑C# Windows窗体应用程序中的多线程调用?,c#,winforms,multithreading,process,performancecounter,C#,Winforms,Multithreading,Process,Performancecounter,我试图让我的C#应用程序多线程,因为有时候,我会遇到一个异常,说我以不安全的方式调用了一个线程。我以前从来没有在程序中做过任何多线程,所以如果我听起来对这个问题有点无知,请容忍我 我的程序概述是,我想做一个性能监控应用程序。这需要使用C#中的进程和性能计数器类来启动和监视应用程序的处理器时间,并将该数字发送回UI。但是,在实际调用性能计数器的nextValue方法(由于计时器设置为每秒执行一次)的方法中,我有时会遇到前面提到的异常,该异常会涉及以不安全的方式调用线程 我附上了一些代码供您阅读。我
private void runBtn_Click(object sender, EventArgs e)
{
// this is called when the user tells the program to launch the desired program and
// monitor it's CPU usage.
// sets up the process and performance counter
m.runAndMonitorApplication();
// Create a new timer that runs every second, and gets CPU readings.
crntTimer = new System.Timers.Timer();
crntTimer.Interval = 1000;
crntTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
crntTimer.Enabled = true;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
// get the current processor time reading
float cpuReading = m.getCPUValue();
// update the current cpu label
crntreadingslbl.Text = cpuReading.ToString(); //
}
// runs the application
public void runAndMonitorApplication()
{
p = new Process();
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = fileName;
p.Start();
pc = new System.Diagnostics.PerformanceCounter("Process",
"% Processor Time",
p.ProcessName,
true);
}
// This returns the current percentage of CPU utilization for the process
public float getCPUValue()
{
float usage = pc.NextValue();
return usage;
}
查看Jon Skeet关于多线程的文章,特别是上的页面。它应该会把你修好的 基本上,您需要检查是否需要调用,然后在需要时执行调用。阅读本文后,您应该能够将UI更新代码重构为如下所示的块:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
// get the current processor time reading
float cpuReading = m.getCPUValue();
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new Action(() => crntreadingslbl.Text = cpuReading.ToString()));
return;
}
// Must be on the UI thread if we've got this far
crntreadingslbl.Text = cpuReading.ToString();
}
在代码中,将需要调用,因为您使用的是计时器。根据以下文件:
已用事件在线程池线程上引发
这意味着您设置为计时器委托的OnTimedEvent()方法将在下一个可用的线程池线程上执行,该线程肯定不是您的UI线程。文档还建议了解决此问题的另一种方法:
如果与用户一起使用计时器
接口元素,如窗体或
控件,指定窗体或控件
它包含到
财产,以便
事件被封送给用户
接口线程
您可能会发现这条路线更容易,但我没有尝试过。我想您的问题在于这条路线:
crntreadingslbl.Text = cpuReading.ToString();
正在UI线程外部运行。不能更新UI线程之外的UI元素。您需要在窗口上调用Invoke来调用UI线程上的新方法
尽管如此,为什么不使用perfmon呢?它是为特定目的而构建的。该组件可能会帮助您。它位于工具箱中,因此您可以拖动到窗体中
此组件公开一组事件,以在不同于UI线程的线程中执行任务。您不必担心创建线程
后台运行的代码和UI控件之间的所有交互都必须通过事件处理程序完成
对于您的场景,您可以设置一个计时器,以在特定的时间间隔触发后台工作程序
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
然后实现适当的事件处理程序来实际收集数据并更新UI
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Collect performance data and update the UI
}
好的,这和背景工作者的评论似乎很有用;但据我所知,进程本身是在UI线程上运行的,但我必须创建一个单独的线程来收集和更新该进程上的数据?一般来说,我怎样才能知道在哪里创建一个单独的线程?当计时器“关闭”时,计时器将在第一个可用的线程池线程上执行请求的ElapsedEventHandler委托。因此,您要求计时器执行的任何操作都将发生在单独的线程上,而不是UI线程上。添加一个后台工作人员只会给等式引入另一个线程。