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

时间:2015-08-08 10:16:51

标签: 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
}

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

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

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

        dtTimeEntriesTemp.Merge(dtEventsTemp);
    }
    else
        BindGridViews();
}

这是我的异步方法。

 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;
 }

在这种方法中,我连接到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" };
    }
}

我认为我对Windows窗体上的异步调用缺少一些东西。我该如何修复我的代码?

3 个答案:

答案 0 :(得分:2)

您的代码存在一些问题

  1. 您将方法标记为async,但您没有等待内部操作。您目前这样做是因为RefreshOrLoadasync void。它实际上需要是async Task,其中底层返回的任务是正在进行的异步操作。然后,应该等待返回的Task

    private async void LogTimeReport_Shown(object sender, EventArgs e)
    {
        // Some syncronous operations
    
        await RefreshOrLoadDataToCache(); // Async methods in it 
    
        // Some syncronous operations
    }
    
  2. RefreshOrLoad是一种异步方法。您使用Task.WhenAll,它用于异步等待多个任务,但您也不会await。然后,您拨打.Resultwhich causes your code to effectively deadlock。所需要的只是等待从LoadTimeEntriesTemp返回的任务:

    private async Task RefreshOrLoadDataToCache()
    {
        if (IsNeededToCallAPI())
        {
            DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();
            DataTable dtEventsTemp = LoadEventsTemp();
    
            dtTimeEntriesTemp.Merge(dtEventsTemp);
        }
        else
            BindGridViews();
    }
    

    我还要注意,您应该使用异步方法使用*Async后缀。

  3. 修复这些内容时,您会发现您的代码按预期运行,一直异步。

答案 1 :(得分:1)

你有问题:

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

DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;

首先,为什么在只有一项任务时会使用Task.WhenAll?然后泄漏Task.WhenAll返回的任务,该任务将完成,并指示传递给Task.WhenAll的所有任务都已完成。然后你等待同步任务导致死锁。 async / await有一个规则,即等待所有方式 所以正确的方法是:

DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();

如果您想要执行与RefreshOrLoadDataToCache();的结果相关的同步操作,您还应等待事件处理程序中的RefreshOrLoadDataToCache();

这是Stephen Cleary Don't Block on Async Code的一篇很棒的文章,它更详细地描述了你的问题。

答案 2 :(得分:0)

方法Task.WhenAll()被标记为异步但它不使用等待LogTimeReport_Shown()private async void RefreshOrLoadDataToCache() { if (IsNeededToCallAPI()) { var taskForTimeEntries = LoadTimeEntriesTemp(); DataTable dtTimeEntriesTemp = await taskForTimeEntries; // call await here DataTable dtEventsTemp = LoadEventsTemp(); dtTimeEntriesTemp.Merge(dtEventsTemp); } else BindGridViews(); } 不必是异步。 :

Index   Name   Value 
1       A      value A
2       B      Value ...
3       A      Value .....
4       C      Value C...