C# 任务上的异步/等待进度事件<&燃气轮机;对象
我对C#5新的C# 任务上的异步/等待进度事件<&燃气轮机;对象,c#,async-await,C#,Async Await,我对C#5新的async/wait关键字完全陌生,我对实现进度事件的最佳方式感兴趣 现在,我更希望进度事件发生在任务本身上。我知道我可以将事件放在包含异步方法的类中,并在事件处理程序中传递某种状态对象,但对我来说,这更像是一种解决方法,而不是解决方案。我可能还希望不同的任务在不同的对象中触发事件处理程序,这听起来很混乱 我有没有办法做类似的事情 var task = scanner.PerformScanAsync(); task.ProgressUpdate += scanner_Progre
async
/wait
关键字完全陌生,我对实现进度事件的最佳方式感兴趣
现在,我更希望进度
事件发生在任务
本身上。我知道我可以将事件放在包含异步方法的类中,并在事件处理程序中传递某种状态对象,但对我来说,这更像是一种解决方法,而不是解决方案。我可能还希望不同的任务在不同的对象中触发事件处理程序,这听起来很混乱
我有没有办法做类似的事情
var task = scanner.PerformScanAsync();
task.ProgressUpdate += scanner_ProgressUpdate;
return await task;
简单地说,
任务
不支持进度。然而,已经有一种传统的方法来实现这一点,即使用接口。基本上,建议重载异步方法(在有意义的情况下)以允许客户端在iproges
实现中传递。然后,您的异步方法将通过它报告进度
Windows运行时(WinRT)API在和类型中确实内置了进度指示器。。。因此,如果您实际上是为WinRT编写的,那么这些都是值得研究的,但也请阅读下面的注释。文档中描述了推荐的方法,它为每个异步方法提供了自己的
IProgress
:
公共异步任务PerformCanAsync(IProgress进程)
{
...
如果(进度!=null)
进度报告(新的MyScanProgress(…);
}
用法:
var progress = new Progress<MyScanProgress>();
progress.ProgressChanged += ...
PerformScanAsync(progress);
var progress=newprogress();
progress.ProgressChanged+=。。。
PerformCanasync(进度);
注:
progress
参数可能是null
,因此一定要在async
方法中检查这一点Progress
的多个调用,不应变异然后重新使用同一arguments对象Progress
类型将捕获构建时的当前上下文(例如,UI上下文),并在该上下文中引发其ProgressChanged
事件。因此,在调用Report
之前,您不必担心封送回UI线程我不得不从几篇文章中拼凑出这个答案,因为我正试图找出如何使这项工作适用于不那么琐碎的代码(即事件通知更改) 让我们假设您有一个同步项目处理器,它将宣布即将开始工作的项目编号。在我的例子中,我只是要操作流程按钮的内容,但是你可以很容易地更新进度条等
private async void BtnProcess_Click(object sender, RoutedEventArgs e)
{
BtnProcess.IsEnabled = false; //prevent successive clicks
var p = new Progress<int>();
p.ProgressChanged += (senderOfProgressChanged, nextItem) =>
{ BtnProcess.Content = "Processing page " + nextItem; };
var result = await Task.Run(() =>
{
var processor = new SynchronousProcessor();
processor.ItemProcessed += (senderOfItemProcessed , e1) =>
((IProgress<int>) p).Report(e1.NextItem);
var done = processor.WorkItWorkItRealGood();
return done ;
});
BtnProcess.IsEnabled = true;
BtnProcess.Content = "Process";
}
private async void BtnProcess\u单击(对象发送方,路由目标)
{
BtnProcess.IsEnabled=false;//防止连续单击
var p=新进展();
p、 ProgressChanged+=(SenderOffProgressChanged,nextItem)=>
{BtnProcess.Content=“处理页面”+nextItem;};
var result=等待任务。运行(()=>
{
var processor=新的SynchronousProcessor();
processor.ItemProcessed+=(senderOfItemProcessed,e1)=>
((IProgress)p.)报告(e1.NextItem);
var done=processor.worktworkitrealgood();
已完成的返回;
});
BtnProcess.IsEnabled=true;
BtnProcess.Content=“流程”;
}
这方面的关键部分是关闭
Progress
订阅中的ItemProcessed
变量。这允许一切正常工作™代码>当使用任务时。运行lambda我使用了其中的调用操作来更新ProgressBar控件。这可能不是最好的方法,但它在紧要关头起作用,而不必进行任何重组
Invoke(new Action(() =>
{
LogProgress();
}));
这就意味着
private void LogProgress()
{
progressBar1.Value = Convert.ToInt32((100 * (1.0 * LinesRead / TotalLinesToRead)));
}
对于WinRT,使用IProgress
(遵循C#约定)编写一个正常的async
方法要容易得多然后使用AsyncInfo.Run
将其包装成IAsyncOperationWithProgress
/iasyncationwithprogress
,而不是直接实现这些接口。@StephenCleary:这取决于您考虑的是调用方还是实现。如果您试图公开功能供其他人调用,我认为WinRT“本机”类型更合适。很明显,两者之间有各种转换器。在C中遵循C约定,在边界处使用包装器(AsTask
/AsyncInfo
)更容易。因此,用C实现的WinRT组件将返回WinRT接口类型,但将使用async
封装在AsyncInfo
@StephenCleary中的常规async
实现它:很公平,我相信你的话:)只是为了让我理解,也许是为了将来帮助其他人。。。你对第二点的推理。如果在Report方法完成之前对传递给Progress.Report()的可变对象进行了更改,则会导致未报告原始值?几乎是这样。我的意思是,当报告
方法完成(并返回)时,实际的报告尚未发生。如果您的T
是可变的,并且您在将其传递到Report
后对其进行了更改,那么您就有了竞争条件。实际报告可能会看到旧值、新值或混合值(即,某些字段的旧值和其他字段的新值)。是的,现在这更有意义了,谢谢。我读了一些你的博客,其中你提到了比赛条件,但直到我读到这个问题后才发现。Thanks那么ProgressChanged委托是否可以按顺序获得Report()'d?根据我的理解,即使传递的对象是不可变的,如果我用
private void LogProgress()
{
progressBar1.Value = Convert.ToInt32((100 * (1.0 * LinesRead / TotalLinesToRead)));
}