在非异步方法中使用异步

本文关键字:异步 异步方法 | 更新日期: 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..
    });
}