Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何以异步方法向UI提供反馈?_C#_.net_Winforms_Asynchronous_Task - Fatal编程技术网

C# 如何以异步方法向UI提供反馈?

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) { //

我一直在开发一个windows窗体项目,我有10个任务要做,我想用
异步
的方式来完成。 当用户单击按钮时,这些任务将启动,我调用
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您仍然只在主线程中更新UI
IProgress.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); 
    });
}