在 Windows 窗体应用程序上异步/等待

5 c# asynchronous async-await

我正在尝试在我的应用程序中学习和实现 async/await 关键字。我正在使用 API 来获取数据,然后将它们显示在我的表单上。当我尝试从控制台应用程序调用方法时,没有问题。但是,如果我从 Form_Shown 事件调用我的异步方法,也没有例外,但方法不起作用。

所以我在 Form_Shown 事件上调用我的 RefreshOrLoadDataToCache() 方法。

private async void LogTimeReport_Shown(object sender, EventArgs e)
{
    // Some syncronous operations

    RefreshOrLoadDataToCache(); // Async methods in it 

    // Some syncronous operations
}
Run Code Online (Sandbox Code Playgroud)

在我的这个方法中创建了一个任务并等待它。

private async void RefreshOrLoadDataToCache()
{
    if (IsNeededToCallAPI())
    {
        var taskForTimeEntries = LoadTimeEntriesTemp();
        Task.WhenAll(taskForTimeEntries);

        DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;
        DataTable dtEventsTemp = LoadEventsTemp();

        dtTimeEntriesTemp.Merge(dtEventsTemp);
    }
    else
        BindGridViews();
}
Run Code Online (Sandbox Code Playgroud)

这是我的异步方法。

 private async Task<DataTable> LoadTimeEntriesTemp()
 { 
     TimeEntryHandler timeHandler = new TimeEntryHandler();

     TimeEntryResponse response = await timeHandler.GetTimeEntries();

     DataTable dt = DatatableHelper.ToDataTable<TimeEntry>(response.TimeEntries);

     foreach (DataRow drow in dt.Rows)
     {
        // Some operations on DataTable
     }
     return dt;
 }
Run Code Online (Sandbox Code Playgroud)

在这种方法中,我连接到 API 并获得结果。我认为我的问题在于这种方法。因为当我从控制台应用程序调用此方法时,它会返回数据。但是从表单应用程序它等待了很长时间但没有结果或异常。

private async Task<TimeEntryResponse> GetTimeEntries()
{
    using (var client = new AuthorizedHttpClient(_client))
    {
        var data = await client.GetAsync<TimeEntryResponse>(parameters);

        if (data.StatusCode == HttpStatusCode.OK)
        {
            var response = (TimeEntryResponse)data.ContentObj;
            response.Pages = int.Parse(data.Headers.GetValues("X-Pages").First());
            response.Page = int.Parse(data.Headers.GetValues("X-Page").First());
            response.TotalRecords = int.Parse(data.Headers.GetValues("X-Records").First());
            return response;
        }
        return new TimeEntryResponse() { TimeEntries = null, STATUS = "ERROR" };
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为我在 Windows 窗体上的异步调用中遗漏了一些东西。如何修复我的代码?

Ham*_*yan 2

你的问题在这里:

var taskForTimeEntries = LoadTimeEntriesTemp();
Task.WhenAll(taskForTimeEntries);

DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;
Run Code Online (Sandbox Code Playgroud)

首先,Task.WhenAll当你只有一个任务时为什么要使用?这样,您就可以泄漏将Task.WhenAll要完成的返回任务,并指示传递给的所有任务Task.WhenAll都已完成。它会同步等待任务,这会导致死锁。

async/await 有一个规则,它以所有方式声明等待 所以正确的方法是:

DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();
Run Code Online (Sandbox Code Playgroud)

RefreshOrLoadDataToCache();另外,如果您想执行与其结果相关的同步操作,则应该在事件处理程序中等待。

这是 Stephen Cleary 撰写的一篇很棒的文章,《不要阻止异步代码》,其中更详细地描述了您的问题。