假设我只想在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
代码的特定部分完成。问题是它不是。当它到达await
中的第一个Load()
时,它只是没有完成加载。调试器变为空白,不会出现其他错误。
是否可以使用非async
方法调用async
方法,就像这样?
有一个原因我不需要这个特定任务async
,而是Load()
函数。
public void GetSomethingElse(){
var task1 = Load().Wait();
}
这怎么可能?
我甚至尝试更改Load()
方法以使用var data = task1.Wait()
等代替await
,无论我尝试哪种方式,仍然没有区别。如果有人能提供帮助,我们将不胜感激。
答案 0 :(得分:11)
你手上可能有僵局。您在需要该线程完成的任务上使用Wait()
阻止线程,因为在ASP.Net中也使用了SynchronizationContext
(也在GUI环境中)。
您应该使用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.
}
注意:通常不建议通过async 进行同步,因为它没有任何好处(你在整个操作过程中阻塞了一个线程)并且可能导致死锁(比如这个)。
答案 1 :(得分:2)
我认为你有一个经典的死锁场景。有关详细信息,请参阅this post。如果您有await
语句,则会在SynchronizationContext
调用之前存储当前await
,然后将其恢复,并将方法的其余部分发布到该Wait()
。在GUI应用程序中,只有一个线程与该上下文相关联,因此该方法的其余部分被认为是在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..
}
是阻塞GUI线程的阻塞调用。
请改为尝试:
GetAsync
如果.ConfigureAwait(false)
内有任何等待,您可能还需要添加{{1}}。
答案 2 :(得分:1)
您可以按如下方式更改您的加载功能:
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..
});
}