在非异步方法中使用异步
本文关键字:异步 异步方法 | 更新日期: 2023-09-27 18:34:21
假设我只想让一个方法在async
中运行。
所以我有一个async
方法,如下所示:
public async Task Load(){
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
}
然后,我尝试在另一个方法中调用该async
方法作为任务,并希望它等到该特定async
段代码完成。问题是它不是。当它到达Load()
的第一个await
时,它只是没有完成加载。调试器变为空白,并且不给出其他错误。
是否可以像这样从非async
方法调用async
方法?我不需要async
这个特定任务是有原因的,但我做的Load()
功能。
public void GetSomethingElse(){
var task1 = Load().Wait();
}
这怎么可能?
我什至尝试将Load()
方法更改为使用 var data = task1.Wait()
等而不是await
,无论我尝试哪种方式,仍然没有区别。 如果有人能帮忙,将不胜感激。
你可能手上有一个死锁。您在需要该线程才能完成的任务上使用Wait()
阻止线程,因为 ASP.Net(也在 GUI 环境中)中使用了SynchronizationContext
。
您应该使用ConfigureAwait(false)
告诉等待者不要捕获该上下文。在第一个await
这样做就足够了,因为其余的没有要捕获SynchronizationContext
:
public async Task Load()
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1.ConfigureAwait(false);
var data2 = await task2;
var data3 = await task3;
//..process data.
}
但是,建议始终使用 ConfigureAwait
,除非您想捕获SynchronizationContext
,因此更好的标准是:
public async Task Load()
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1.ConfigureAwait(false);
var data2 = await task2.ConfigureAwait(false);
var data3 = await task3.ConfigureAwait(false);
//..process data.
}
在您的情况下,如果要在所有任务完成后继续,则应使用Task.WhenAll
而不是单独await
每个任务:
public async Task Load()
{
await Task.WhenAll(GetAsync(1), GetAsync(2), GetAsync(3)).ConfigureAwait(false);
// process data.
}
注意:通常不鼓励通过异步进行同步,因为它没有任何好处(在整个操作过程中阻塞线程),并且可能会导致死锁(像这个一样)。
我认为你有一个经典的死锁场景。有关更多详细信息,请参阅此帖子。当您具有 await
语句时,当前SynchronizationContext
存储在await
调用之前,并在之后还原,并将方法的其余部分发布到该语句。在 GUI 应用程序中,只有一个线程与该上下文相关联,因此该方法的其余部分被指定在 GUI 线程上执行,但它不能因为Wait()
是阻塞 GUI 线程的阻塞调用。
试试这个:
public async Task Load(){
Task task1 = GetAsync(1).ConfigureAwait(false);
Task task2 = GetAsync(2).ConfigureAwait(false);
Task task3 = GetAsync(3).ConfigureAwait(false);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
}
如果GetAsync
里面有任何等待,您可能也必须在那里添加.ConfigureAwait(false)
。
您可以按如下方式更改 Load 函数:
public async Task Load(){
await new TaskFactory().StartNew(() =>
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
});
}