我是TPL的新手,我正在尝试使用并行性测试一个非常简单的应用程序。
我正在使用WinForms,C#,VS2015
在我的表格中,我有一个进度条,一个计时器和一个计量器。 我正在使用 Infragistics 15.2 控件。
PerformanceCounter
timer.Tick
(我设置500ms间隔),我从PerformanceCounter读取CPU使用情况并更新Gauge控件的值。我的问题是第一次访问CPU计数器的NextValue()实际上是非常昂贵的,并冻结了UI。
我期待有一个完整的响应式用户界面,但它仍然冻结。我肯定错过了一些东西,但我找不到什么。
我非常确定阻止操作是NextValue()
:如果我用随机数生成器替换它,我的UI就会完全响应。
你能帮帮我吗?
public partial class Form1 : Form
{
PerformanceCounter _CPUCounter;
public Form1()
{
InitializeComponent();
}
private async void ultraButton1_Click(object sender, EventArgs e)
{
var progress = new Progress<int>(valuePBar => {ultraProgressBar1.Value = valuePBar; });
await Task.Run(() => UpdatePBar(progress));
}
private void Form1_Load(object sender, EventArgs e)
{
Task.Run(() =>
{
_CPUCounter = new PerformanceCounter();
_CPUCounter.CategoryName = "Processor";
_CPUCounter.CounterName = "% Processor Time";
_CPUCounter.InstanceName = "_Total";
BeginInvoke(new Action(() =>
{
timer1.Interval = 500;
timer1.Start();
}));
});
}
private async void timer1_Tick(object sender, EventArgs e)
{
float usage = await Task.Run(() => _CPUCounter.NextValue());
RadialGauge r_gauge = (RadialGauge)ultraGauge1.Gauges[0];
r_gauge.Scales[0].Markers[0].Value = usage;
}
public void UpdatePBar(IProgress<int> progress)
{
for (Int32 seconds = 1; seconds <= 10; seconds++)
{
Thread.Sleep(1000); //simulate Work, do something with the data received
if (seconds != 10 && progress != null)
{
progress.Report(seconds * 10);
}
}
}
}
答案 0 :(得分:2)
你应该:
Task.Run
Task.Factory.StartNew
。async
/ await
,如果您希望&#34;返回UI线程&#34;。IProgress<T>
进行更新。TaskScheduler
或SynchronizationContext
s(此处不需要它们)。Control.BeginInvoke
或Control.Invoke
。在这种情况下,您的计时器可以使用NextValue
在线程池线程上运行Task.Run
,然后使用结果值更新UI:
private async void OnTimerTickElapsed(Object sender, EventArgs e)
{
float usage = await Task.Run(() => _CPUCounter.NextValue());
RadialGauge r_gauge = (RadialGauge)this.ultraGauge.Gauges[0];
r_gauge.Scales[0].Markers[0].Value = usage;
}
对于您的进度条,让MyMethod
选择IProgress<int>
:
private void ButtonClick(Object sender, EventArgs e)
{
var progress = new Progress<int>(valuePBar => { this.progressBar.Value = valuePBar; });
MyMethod(progress);
}
MyMethod(IProgress<int> progress)
{
...
progress.Report(50);
...
}
答案 1 :(得分:1)
三件事......
Task.Run(() => { // progress bar code }
CPUCounter
不是static
_CPUCounter.NextValue()
方法支持异步并等待,否则会阻止。我也赞成Task.Run
超过Task.Factory.StartNew
,因为它有更好的默认值(内部调用Task.Factory.StartNew
)
private void OnTimerTickElapsed(Object sender, EventArgs e)
{
await Task.Run(async () =>
{
float usage = await _CPUCounter.NextValue();
RadialGauge r_gauge = (RadialGauge)this.ultraGauge.Gauges[0];
r_gauge.Scales[0].Markers[0].Value = usage;
}, TaskCreationOptions.LongRunning);
}
查看您正在使用的间隔。对于TPL的开销,50ms有点快。
答案 2 :(得分:1)
尝试了您的代码(只是用显示usage
变量的简单标签替换了量表,当然没有点击按钮,因为您没有提供MyMethod
)并且没有& #39;体验任何UI阻止。
发布用于更新UI的正确代码(即使使用UI调度程序IMO,使用Task
也是一种过度杀伤):
private void UpdateProgressBar(Int32 valuePBar)
{
BeginInvoke(new Action(() =>
{
this.progressBar.Value = valuePBar;
}));
}
private void OnTimerTickElapsed(Object sender, EventArgs e)
{
Task.Run(() =>
{
float usage = _CPUCounter.NextValue();
BeginInvoke(new Action(() =>
{
RadialGauge r_gauge = (RadialGauge)this.ultraGauge.Gauges[0];
r_gauge.Scales[0].Markers[0].Value = usage;
}));
});
}
但是,代码中没有明显的原因会导致UI阻塞。 最后这部分除外
_CPUCounter = new PerformanceCounter();
_CPUCounter.CategoryName = "Processor";
_CPUCounter.CounterName = "% Processor Time";
_CPUCounter.InstanceName = "_Total";
在OnFormLoad
的UI线程上运行。您可以尝试在这样的单独任务上移动该代码
private void OnFormLoad(Object sender, EventArgs e)
{
Task.Run(() =>
{
_CPUCounter = new PerformanceCounter();
_CPUCounter.CategoryName = "Processor";
_CPUCounter.CounterName = "% Processor Time";
_CPUCounter.InstanceName = "_Total";
BeginInvoke(new Action(() =>
{
this.timer.Interval = 500;
this.timer.Start();
}));
});
}
答案 3 :(得分:1)
Somebody has already investigated this problem
问题是PerformanceCounter初始化很复杂并且需要花费很多时间。但不清楚的是,这个初始化是阻止所有线程还是只是拥有线程(创建PerformanceCounter的线程)。
但我认为您的代码已经提供了答案。由于您已经在线程池线程中创建了PerformanceCounter,它仍然会阻止UI。假设初始化阻塞所有线程可能是正确的。
如果初始化阻止所有线程,那么解决方案是在应用程序启动时初始化PerformanceCounter。通过不使用默认构造函数(选择不仅构造实例而且还初始化它的构造函数)或在启动期间调用NextValue一次。