C# 如何以异步方法向UI提供反馈?
我一直在开发一个windows窗体项目,我有10个任务要做,我想用C# 如何以异步方法向UI提供反馈?,c#,.net,winforms,asynchronous,task,C#,.net,Winforms,Asynchronous,Task,我一直在开发一个windows窗体项目,我有10个任务要做,我想用异步的方式来完成。 当用户单击按钮时,这些任务将启动,我调用async方法来执行此操作。在我的代码中,我已经有了这些进程的参数列表 我的问题是: A) 如何将代码转换为并行运行所有进程?(我想实现异步/等待) B) 如何向我的UI应用程序提供反馈 下面的代码是我尝试过的: 我的按钮调用一个方法来启动进程 private void button1_Click(object sender, EventArgs e) { //
异步
的方式来完成。
当用户单击按钮时,这些任务将启动,我调用async
方法来执行此操作。在我的代码中,我已经有了这些进程的参数列表
我的问题是:
A) 如何将代码转换为并行运行所有进程?(我想实现异步/等待)
B) 如何向我的UI应用程序提供反馈
下面的代码是我尝试过的:
我的按钮调用一个方法来启动进程
private void button1_Click(object sender, EventArgs e)
{
// almost 15 process
foreach (var process in Processes)
{
// call a async method to process
ProcessObject(process);
}
}
模拟获取参数的过程的方法
private async void ProcessObject(ProcessViewModel process)
{
// this is my loop scope, which I need to run in parallel
{
// my code is here
// increment the progress of this process
process.Progress++;
// feedback to UI (accessing the UI controls)
UpdateRow(process);
}
}
我尝试过这样做,但我不确定这是否是更新我的UI(网格)的正确方法
首先,对
异步void
方法(事件处理程序是异常)说no,因为不会注意到其中抛出的异常。而是使用异步任务
并等待它
private async void button1_Click(object sender, EventArgs e)// <--Note the async modifier
{
// almost 15 process
foreach (var process in Processes)
{
// call a async method to process
await ProcessObject(process);
}
}
private async Task ProcessObject(ProcessViewModel process)// <--Note the return type
{
// my code is here with some loops
await Task.Run(()=>
{
//Will be run in ThreadPool thread
//Do whatever cpu bound work here
});
//At this point code runs in UI thread
process.Progress++;
// feedback to UI
UpdateRow(process);
}
private async void button1_Click(object sender, EventArgs e)// <--Note the async modifier
{
var tasks = Processes.Select(ProcessObject).ToList();
await Task.WhenAll(tasks);
}
Sriram介绍了如何进行异步/等待,但是我想向您展示另一种方法,以他的答案为基础更新UI线程的进度 .NET4.5添加了接口和类。内置的Progress类捕获同步上下文,就像async/await一样,然后当您报告进度时,它使用该上下文进行回调(在您的例子中是UI线程)
private async void按钮1\u单击(对象发送方,事件参数e)
{
var progress=new progress(UpdateRow);//当报告进度时,会回调UpdateRow。
var tasks=processs.Select(process=>ProcessObject(process,progress)).ToList();
等待任务。何时(任务);
}
私有异步任务ProcessObject(ProcessViewModel进程,IProgress进程)
{
//我的代码中有一些循环
等待任务。运行(()=>
{
//将在线程池线程中运行
//在这里做任何cpu限制的工作
//仍在线程池线程中
进程.进程++;
//对UI的反馈,在UI线程上调用UpdateRow。
进度报告(过程);
});
}
您没有在等待ProcessObject
。打字错误或..?不,我只是调用ProcessObject方法。我没有此方法的结果,是否应将其更改为wait?这也可以通过TPL完成:1)如果使用async,则在事件处理程序中一直使用它。2) 除事件处理程序外,切勿使用async void
。3) Async With Waitis是完全同步的。Filipe,这是Winforms还是WPF?我需要将UpdateRow调用放在任务中。运行,因为我将在这个进程方法中有一个循环。我在这里尝试过,但是我得到一个错误,说MyDataGridView控件可以被线程访问,因为它是由另一个线程创建的。我试图在参数中传递dataGridView的引用,但不起作用,您知道我如何解决这个问题吗?如果你不介意发布原始代码,谢谢你的回复。我已经根据您的代码进行了回答,但现在您说需要在Task.Run
中使用UpdateRow
。那不行。它必须在主线程中调用。还有,为什么你需要一个循环呢?请提供更多信息。我看到了你的编辑,用这些信息我不能提供一个好的答案。我需要知道用户界面的哪个部分需要更新,以及如何更新等等。还有为什么你需要平行跑?你是在做杯子绑定操作还是IO绑定操作?需要回答更多细节。如果这是Winforms,那么您可以调用dataGridView1的BeginInvoke方法,然后传入一个委托来执行更新。dataGridView1.BeginInvoke(新操作(()=>{//makecallstoupdatetheUI}));不,不要那样做。我不推荐。如果您需要调用BeginInvoke
为什么首先要使用async
方法?斯科特:我想到了这一点,但这在异步方法中没有什么用处。当异步方法恢复时,您可以直接在ProcessObject
内部调用UpdateRow
。@SriramSakthivel我同意您的方法是更好的方法(这就是为什么您的答案是我在回答中提到的第一件事),但OP似乎对此有抵抗力,我只想给其他选项Summ,+1作为您的其他选项:)@Scott,谢谢你的来信。我在这里试过,效果很好。为了理解,现在我可以访问UI控件了。为什么Sriram代码不能?建议在我的参数(ProcessViewModel)中添加对UI控件的引用,并从参数中使用它,而不是直接从控件访问?谢谢。@FelipeOriani您仍然只在主线程中更新UIIProgress.Report
将在内部将控件传递给UI线程UpdateRow
将在UI线程中运行,这就是它的美妙之处。我建议你读书。如果您感兴趣,请阅读stephen关于TPL的所有帖子。他是这方面的专家。还要注意,您不能从另一个线程更新UI。如果这样做,您将得到InvalidOperationException
。
private async void button1_Click(object sender, EventArgs e)// <--Note the async modifier
{
var tasks = Processes.Select(ProcessObject).ToList();
await Task.WhenAll(tasks);
}
private async void button1_Click(object sender, EventArgs e)
{
var progress = new Progress<ProcessViewModel>(UpdateRow); //This makes a callback to UpdateRow when progress is reported.
var tasks = Processes.Select(process => ProcessObject(process, progress)).ToList();
await Task.WhenAll(tasks);
}
private async Task ProcessObject(ProcessViewModel process, IProgress<ProcessViewModel> progress)
{
// my code is here with some loops
await Task.Run(()=>
{
//Will be run in ThreadPool thread
//Do whatever cpu bound work here
//Still in the thread pool thread
process.Progress++;
// feedback to UI, calls UpdateRow on the UI thread.
progress.Report(process);
});
}