简体   繁体   English

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

[英]Async / await on windows form application

I'm trying to learn and implement async / await keywords on my application.我正在尝试在我的应用程序中学习和实现 async/await 关键字。 I'm using an API to get data then showing them on my forms.我正在使用 API 来获取数据,然后将它们显示在我的表单上。 When I try to call methods from an console application there is no problem.当我尝试从控制台应用程序调用方法时,没有问题。 But if I call my async methods from Form_Shown event also there no exception but methods not working.但是,如果我从 Form_Shown 事件调用我的异步方法,也没有例外,但方法不起作用。

So I'm calling my RefreshOrLoadDataToCache() method on Form_Shown event.所以我在 Form_Shown 事件上调用我的 RefreshOrLoadDataToCache() 方法。

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

    RefreshOrLoadDataToCache(); // Async methods in it 

    // Some syncronous operations
}

In my this method created a task and wait for it.在我的这个方法中创建了一个任务并等待它。

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

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

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

This my async method.这是我的异步方法。

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

In this method I'm connecting to API and getting results.在这种方法中,我连接到 API 并获得结果。 I think my problem is about this method.我认为我的问题在于这种方法。 Because when I call this method from console application it returns data.因为当我从控制台应用程序调用此方法时,它会返回数据。 But from form application it waits for a long time but there is no result or exception.但是从表单应用程序它等待了很长时间但没有结果或异常。

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

I thought that there is something I'm missing about asyncronous calls on windows forms.我认为我在 Windows 窗体上的异步调用中遗漏了一些东西。 How can I fix my code ?如何修复我的代码?

You have a couple of problems with your code你的代码有几个问题

  1. You mark a method as async , but you don't await on the operation inside.您将方法标记为async ,但您不会等待内部的操作。 You currently do this because RefreshOrLoad is async void .您目前这样做是因为RefreshOrLoadasync void It actually needs to be async Task , where the underlying returned task is the ongoing async operation.它实际上需要是async Task ,其中底层返回的任务是正在进行的异步操作。 Then, the returned Task should be awaited on:然后,应该等待返回的Task

     private async void LogTimeReport_Shown(object sender, EventArgs e) { // Some syncronous operations await RefreshOrLoadDataToCache(); // Async methods in it // Some syncronous operations }
  2. RefreshOrLoad is an async method. RefreshOrLoad是一种异步方法。 You use Task.WhenAll , which is used for asynchronously waiting on multiple tasks, but you don't await on it either.您使用Task.WhenAll ,它用于异步等待多个任务,但您也不await它。 Then, you call .Result , which causes your code to effectively deadlock .然后,您调用.Result这会导致您的代码有效地死锁 All that's needed is to await the task returning from LoadTimeEntriesTemp :所需要的只是等待从LoadTimeEntriesTemp返回的任务:

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

    I'd also note that you should use the *Async postfix with your async methods.我还注意到您应该在异步方法中使用*Async后缀。

When fixing these, you'll see that your code behaves as expected, being asynchronous all the way down.修复这些时,您会看到您的代码按预期运行,一直是异步的。

You problem here:你的问题在这里:

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

DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;

At first, why do you use Task.WhenAll when you have just one task?首先,当您只有一个任务时,为什么要使用Task.WhenAll That way you leak the task returned by Task.WhenAll which will be completed and indicate that all your tasks that are passed to Task.WhenAll are completed.这样你就泄露了Task.WhenAll返回的任务,该任务将完成,并表明你传递给Task.WhenAll所有任务都已完成。 It will wait for the task synchronously which will cause deadlock.它将同步等待任务,这将导致死锁。

There is a rule for async/await which states await in all ways So right approach is: async/await 有一个规则,它以所有方式等待状态所以正确的方法是:

DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();

Also, you should await on RefreshOrLoadDataToCache();此外,您应该等待RefreshOrLoadDataToCache(); in your event handler if you want to do synchronous operations related to its result.如果您想执行与其结果相关的同步操作,请在您的事件处理程序中。

Here is a great article by Stephen Cleary Don't Block on Async Code which describes your problem in more details.这是 Stephen Cleary Don't Block on Async Code 的一篇很棒的文章,它更详细地描述了您的问题。

Method RefreshOrLoadDataToCache() is marked as async yet it does not use await on Task.WhenAll() and LogTimeReport_Shown() does not have to be async.方法RefreshOrLoadDataToCache()被标记为异步,但它不会在Task.WhenAll()上使用 await,并且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();
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM