在非异步方法中使用async

时间:2014-12-15 05:56:15

标签: c# asp.net .net asynchronous async-await

假设我只想在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,无论我尝试哪种方式,仍然没有区别。如果有人能提供帮助,我们将不胜感激。

3 个答案:

答案 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..
    });
}