进展如何?与Action< T>不同? (C#) [英] How is Progress<T> different from Action<T> ? (C#)
问题描述
我一直在使用 Progress< T>
,想知道它是否可以由 Action< T>
代替。
I've been using Progress<T>
and wondered if it can be replaced by Action<T>
.
在下面的代码中,使用它们中的每一个来报告进度,即 ReportWithProgress()
或 ReportWithAction()
,对我没有任何明显的影响。 progressBar1
的增加方式,字符串在输出窗口上的写入方式,看起来似乎相同。
In the code below, using each of them for reporting progress, i.e. ReportWithProgress()
or ReportWithAction()
, didn't make any noticeable difference to me. How progressBar1
increased, how the strings were written on the output window, they seemed the same.
// WinForm application with progressBar1
private void HeavyIO()
{
Thread.Sleep(20); // assume heavy IO
}
private async Task ReportWithProgress()
{
IProgress<int> p = new Progress<int>(i => progressBar1.Value = i);
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
p.Report(i);
}
}
private async Task ReportWithAction()
{
var a = new Action<int>(i => progressBar1.Value = i);
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Action : " + i);
a(i);
}
}
但是 Progress< T>
不能完全改变方向。实施它应该有一个原因。谷歌搜索 c#Progress vs Action并没有给我太多帮助。进度与操作有何不同?
But Progress<T>
can't be a reinvention of the wheel. There should be a reason why it was implemented. Googling "c# Progress vs Action" didn't give me much help. How is Progress different from Action?
推荐答案
调用 progressBar1.Value = i
来自不同线程的结果将导致可怕的跨线程操作无效 异常。另一方面, Progress
类将事件调度到同步上下文在构建时捕获:
Calling progressBar1.Value = i
from a different thread results in the dreaded "cross-thread operation not valid" exception. The Progress
class, on the other hand, dispatches the event to the synchronization context captured in the moment of construction:
// simplified code, check reference source for actual code
void IProgress<T>.Report(T value)
{
// post the processing to the captured sync context
m_synchronizationContext.Post(InvokeHandlers, value);
}
private void InvokeHandlers(object state)
{
// invoke the handler passed through the constructor
m_handler?.Invoke((T)state);
// invoke the ProgressChanged event handler
ProgressChanged?.Invoke(this, (T)state);
}
这可确保完成对进度条,标签和其他UI元素的所有更新在一个(唯一的)GUI线程上。
This ensures that all updates to progress bars, labels and other UI elements are done on a (one and only) GUI thread.
因此,仅实例化 Progress
类< 在后台线程的strong>外部内部,在UI线程上被调用:
So, it only makes sense to instantiate the Progress
class outside of the background thread, inside a method which is called on a UI thread:
void Button_Click(object sender, EventArgs e)
{
// since this is a UI event, instantiating the Progress class
// here will capture the UI thread context
var progress = new Progress<int>(i => progressBar1.Value = i);
// pass this instance to the background task
Task.Run(() => ReportWithProgress(progress));
}
async Task ReportWithProgress(IProgress<int> p)
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
p.Report(i);
}
}
这篇关于进展如何?与Action< T>不同? (C#)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!