dix*_*hom 5 c# generics delegates progress
我一直在使用Progress<T>,想知道是否可以将其替换Action<T>。
在下面的代码中,使用它们中的每个来报告进度(即ReportWithProgress()或)ReportWithAction()对我没有任何明显的影响。如何progressBar1增加字符串,如何将字符串写入输出窗口,它们看起来是相同的。
// 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);
}
}
Run Code Online (Sandbox Code Playgroud)
但是Progress<T>,这不能彻底改变方向。实施它应该有一个原因。谷歌搜索“ c#Progress vs Action”并没有给我太多帮助。进展与行动有何不同?
Gro*_*roo 15
progressBar1.Value = i从不同的线程调用会导致可怕的“跨线程操作无效”异常。该Progress班,在另一方面,调度该事件的同步环境建设的瞬间捕捉到:
// 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);
}
Run Code Online (Sandbox Code Playgroud)
这确保了对进度条、标签和其他 UI 元素的所有更新都在(一个且唯一的)GUI 线程上完成。
因此,它才有意义,以实例化Progress类以外的后台线程,一个被称为在UI线程方法中:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
不同之处在于,Progress<T>您有一个事件,其中多个侦听器可以侦听进度,并在构造实例时Progress<T>捕获该事件SynchonizationContext,因此如果在 GUI 线程中创建,则不需要调用 GUI 线程。
您还可以向 an 添加多个侦听器Action<T>(感谢 @Servy 指出这一点),但每个侦听器都会在调用该操作的线程中执行。
考虑下面的扩展示例,其中Progress<T>会起作用,但Action<T>会抛出异常:
private async Task ReportWithProgress()
{
var p = new Progress<int>(i => progressBar1.Value = i);
p.ProgressChanged += (s, e) => progressBar2.Value = e;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
((IProgress<int>)p).Report(i);
}
});
}
private async Task ReportWithAction()
{
var a = new Action<int>(i => progressBar1.Value = i);
a += i => progressBar2.Value = i;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Action : " + i);
a(i);
}
});
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
567 次 |
| 最近记录: |