如何调用异步方法,并从那里更新UI?

时间:2014-08-08 02:30:13

标签: c# .net user-interface task-parallel-library async-await

最近我已经完成了PagedDataGridView自定义控件的设计,它运行得很好,但现在我想提高它的性能。

怎么样?好吧,我在显示新页面时看到了一些UI阻止 简而言之,这就是:

public class PagedDataGridView() : UserControl
{
    Paging paging = new Paging();

    public PagedDataGridView()
    {
        paging.LoadDataClientMethod = LoadDataOnGrid;
    }

    private void LoadDataOnGrid()
    {
        // Some heavy set data source here, using functions from 'paging' object
    }
}


我想做的事情(使用async / await模式): 异步方法DoPaging暂停,直到等待'LoadDataOnGrid'完成后,UI线程不会被阻塞,这是异步的

// Class that handles paging methods, variables,
// also offers navigation controls, such as buttons, labels, etc.
internal class Paging
{
    // Represents the method that code client uses to load its own data
    public Action LoadDataClientMethod;

    // HERE: 
    private async Task DoPaging()
    {
        // some calculations

        if (LoadDataClientMethod != null)
        {
            // I realizad that calling Run method, runs it out of context
            // How to run this method, and update the UI
            await Task.Run(() => LoadDataClientMethod());
        }

        // Update controls, showing current page, etc
        UpdateUI();
    }

    // Navigation buttons
    private void btnGoNextPage(object sender, EventArgs e)
    {
        // go next page calculations

        // Then how to call the async method
        DoPaging(); // -> doing this, VS shows a warning:
        /* Because this call is not awaited, the current method
           continues to run before the call is completed */
    }
}

我刚刚开始学习异步 - 等待编码,任何更正或建议将不胜感激,谢谢。

2 个答案:

答案 0 :(得分:1)

只需将async添加到按钮点击事件处理程序方法,然后等待对DoPaging()的调用:

private async void btnGoNextPage(object sender, EventArgs e)
{
    await DoPaging();
}

像这样做而不是你的方式给出warning(实际上是为什么给出警告)之间的区别在于,如果你在调用{{1}之后添加了任何代码在事件处理程序中,它现在将在任务完成后发生,而在调用之后立即执行。

答案 1 :(得分:1)

之间存在很大差异:

private void btnGoNextPage(object sender, EventArgs e)
{
    DoPaging();
}

private async void btnGoNextPage(object sender, EventArgs e)
{
    await DoPaging();
}

异常处理。如果前者抛出异常,可能会发生两件事:

  1. 如果您使用的是.NET 4.0,那么吞并的任务将从Finalizer主题重新抛出,并导致您的应用程序崩溃
  2. 如果您使用的是.NET 4.5,那么该任务将被吞下并且不会被注意到,并且将不会被重新抛出,因此可能会在损坏的状态下进入您的应用程序你不会意识到的。
  3. 在后一个示例中,异常将传播到await点,您可以通过添加try-catch块来优雅地处理它。

    作为旁注,我在评论中告诉你正在做什么样的工作阻塞你的UI线程,你说你正在调用你的数据库来检索数据。 针对数据库进行的工作是IO绑定工作,并且大多数提供程序公开异步端点以访问数据,例如实体框架,ADO.NET等。您可以通过不使用任何线程池线程来执行自然异步行为。适合你(在你的例子中使用Task.Run)。当你“一直异步”时,你可以这样做,你的数据库查询可以与await关键字一起使用。这样,当查询检索数据时,调用查询的线程(在您的示例中,UI线程)被释放并且可以执行更多工作,因此您的UI将保持响应。

    我建议你研究一下,看看你的数据库提供者是否有那些异步端点。