我有两种方法需要运行异步。为了Method B()
运行,它会调用Method A()
,但有时Method A()
会在其他地方调用。在Background Worker completed事件中,我更新了UI,如果从主线程调用Method A()
,一切都很好。但是,当Method A()
内部调用Method B()
时,RunWorkerCompleted
的{{1}}现在发生在Method A()
的主题而不是主线程上。我能想到的唯一解决方案是在主线程上实现事件监听器,然后在Method B()
的{{1}}方法中,引发此事件。任何人都可以告诉我如何实现这一目标,或者是否有更好的方法来处理这个问题?
答案 0 :(得分:0)
BGW已经过时了。 其所有功能已被async/await
,Task.Run,IProgress界面和CancellationToken/CancellationTokenSource取代。 .NET博客帖子Async in 4.5: Enabling Progress and Cancellation in Async APIs显示了在托管线程和异步API中如何进行和取消。
鉴于最早支持的.NET版本是4.5.2,您可以确信async/await
始终可用。
BGW过时的原因之一是你无法嵌套或组合BGW。 BGW是在第一个.NET版本中引入的,用于以形式的背景执行简单的工作/这就是为什么它是System.ComponentModel
的一部分并引发接口。
创建一个执行某些繁重处理的方法和异步调用REST API对async/await
来说是微不足道的:
HttpClient _restClient=new HttpClient();
async Task MethodA(int someParam)
{
toolStripLabel.Text ="Starting ...";
var result=await Task.Run(()=>FunctionB(someParam));
toolStripLabel.Text ="Calling ...";
var response=await _restClient.GetAsync(${baseUrl}/{result}");
toolStripLabel.Text ="Finished";
this.TextBox1.Text=response;
}
Task.Run
将在后台线程中运行FunctionB
。 await
释放当前的UI线程,直到Task.Run
生成的任务完成。一旦执行,执行将返回到UI线程。用户界面可以直接修改。
调用HttpClient或任何其他异步API的异步方法不在后台线程上运行 - 这样的IO操作在操作系统级别通过IO完成端口异步处理 - 大致是在调用时调用的回调IO操作完成。操作系统不会阻止等待磁盘或网络响应。
如果FunctionB
运行太久以至于需要一些进度报告,该怎么办?
我们可以使用复杂的类来报告进度,或者只是将中间结果发送给正在收听的人:
public class Report
{
public string Message{get;set;}
public string Status{get;set;}
public int Progress{get;set;}
//Why not?
public Color MessageColor{get;set;}
Report(string message,string status, int progress, Color color)
{
...
}
}
在这种情况下,Progress<T>
只会调用此方法来更新UI并记录任何消息:
void ReportProgress(Report report)
{
//Doesn't have to be UI only
Log.Verbose(report.Message);
toolStripLabel.Text =report.Status;
toolStripLabel.BackColor=report.Color;
toolStripProgress.Value=report.Progress;
}
MethodA
本身可以使用Progress< T>
将自己与用户界面分开:
async Task MethodA(int someParam)
{
IProgress<Report> progress=new Progress<Report>(ReportProgress)
progress.Report(new Report("Starting","Starting",0,Color.White);
var result=await Task.Run(()=>FunctionB(someParam,progress));
progress.Report(new Report("Calling API","Calling",95,Color.Aqua);
var response=await _restClient.GetAsync(${baseUrl}/{result}");
progress.Report(new Report("Finished progessing","Finished",100,Color.White);
this.TextBox1.Text=response;
}
和FunctionB
可以报告这样的进展:
int FunctionB(int someParam,IProgress<Report> progress)
{
for (int i=0;i<10000000; i++)
{
//Do the work and report
if (i % 100000 ==0)
{
var value=(int)(i / 100000.0);
progress.Report(new Report("Blah blah","Processing",value,Color.Pink);
}
progress.Report(new Report("Blah blah","Processing Finished",100,Color.Red);
}
}
答案 1 :(得分:-1)
您还可以调用需要更新的UI元素。这看起来像这样:
textBox1.Invoke((MethodInvoker) delegate { textBox1.Text = "TEST"; });