【问题标题】:Task.WaitAll hanging with multiple awaitable tasks in ASP.NETTask.WaitAll 在 ASP.NET 中挂起多个等待任务
【发布时间】:2012-10-10 11:53:27
【问题描述】:

以下是我遇到问题的代码的简化版本。当我在控制台应用程序中运行它时,它按预期工作。所有查询都是并行运行的,Task.WaitAll() 在它们全部完成后返回。

但是,当此代码在 Web 应用程序中运行时,请求会挂起。当我附加一个调试器并全部中断时,它表明执行正在等待Task.WaitAll()。第一个任务已经完成,但其他任务从未完成。

我不知道为什么它在 ASP.NET 中运行时挂起,但在控制台应用程序中运行良好。

public Foo[] DoWork(int[] values)
{
    int count = values.Length;
    Task[] tasks = new Task[count];

    for (int i = 0; i < count; i++)
    {
        tasks[i] = GetFooAsync(values[i]);
    }

    try
    {
        Task.WaitAll(tasks);
    }
    catch (AggregateException)
    {
        // Handle exceptions
    }

    return ...
}

public async Task<Foo> GetFooAsync(int value)
{
    Foo foo = null;

    Func<Foo, Task> executeCommand = async (command) =>
    {
        foo = new Foo();

        using (SqlDataReader reader = await command.ExecuteReaderAsync())
        {
            ReadFoo(reader, foo);
        }
    };

    await QueryAsync(executeCommand, value);

    return foo;
}

public async Task QueryAsync(Func<SqlCommand, Task> executeCommand, int value)
{
    using (SqlConnection connection = new SqlConnection(...))
    {
        connection.Open();

        using (SqlCommand command = connection.CreateCommand())
        {
            // Set up query...

            await executeCommand(command);

            // Log results...

            return;
        }
    }           
}

【问题讨论】:

    标签: c# asp.net task-parallel-library async-await


    【解决方案1】:

    您需要使用await Task.WhenAll,而不是Task.WaitAll

    在 ASP.NET 中,您有一个实际的同步上下文。这意味着在所有await 调用之后,您将被封送回该上下文以执行延续(有效地序列化这些延续)。在控制台应用程序中没有同步上下文,因此所有的延续都只是发送到线程池。通过在请求的上下文中使用Task.WaitAll,您将阻止它,从而阻止它被用于处理所有其他任务的延续。

    另请注意,ASP 应用程序中异步/等待的主要好处之一是阻塞您用来处理请求的线程池线程。如果你使用Task.WaitAll,你就违背了这个目的。

    进行此更改的副作用是,通过从阻塞操作转移到等待操作,异常会以不同方式传播。它不会抛出AggregateException,而是抛出底层异常之一。

    【讨论】:

    • +1。虽然我会使用术语“请求上下文”而不是“主线程”。
    • 一个重要的警告是 await Task.WhenAll 不会抛出 AggregateException;只有一个内部异常会被传播。如果要检查整个 AggregateException,则需要存储对从 task.WhenAll 返回的任务的引用并明确检查其 Exception 属性。
    • @sectrean 如果是这种情况,那么您不妨完全删除所有 async/await 代码,然后使用阻塞方法依次执行所有操作。
    • @sectrean 是的,这本质上就是使用 async/await 的效果,所有获取或使用异步操作结果的东西都是任务,除非你从上到下进行更深入的重构.
    • @Servy 在 ASP.NET 中 await 之后,您返回到相同的上下文,但它可能在不同的线程上。所以我认为你不能真正将其称为“该请求的主线程”,因为哪个线程可以在请求的整个生命周期内发生变化。
    猜你喜欢
    • 1970-01-01
    • 2016-02-09
    • 1970-01-01
    • 2015-11-16
    • 2013-11-19
    • 2012-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多