[英]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你的代码有几个问题
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
.您目前这样做是因为
RefreshOrLoad
是async 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 }
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.