Task.WaitAll挂在ASP.NET多个awaitable任务 [英] Task.WaitAll hanging with multiple awaitable tasks in ASP.NET

查看:447
本文介绍了Task.WaitAll挂在ASP.NET多个awaitable任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是我遇到的麻烦的code的简化版本。当我在一个控制台应用程序运行它,它按预期工作。所有查询都并行运行和 Task.WaitAll()时,他们都完全的回报。

然而,当这code在运行Web应用程序,请求只是挂起。当我附加一个调试器,打破一切,这表明执行是伺候 Task.WaitAll()。和第一个任务已经完成,但其他人永远不会结束。

我想不通为什么它在ASP.NET运行时挂起,但在一个控制台应用程序工作正常。

 公共富[]的DoWork(INT []值)
{
    诠释计数= values​​.Length;
    任务[] =任务新的Task [统计]    的for(int i = 0; I<计数;我++)
    {
        任务[I] = GetFooAsync(值[I]);
    }    尝试
    {
        Task.WaitAll(任务);
    }
    赶上(AggregateException)
    {
        //处理异常
    }    返回...
}公共异步任务<富> GetFooAsync(int值)
{
    富富= NULL;    FUNC<富,任务> executeCommand =异步(命令)=>
    {
        富=新的Foo();        使用(SqlDataReader的读卡器=等待command.ExecuteReaderAsync())
        {
            ReadFoo(读卡器,富);
        }
    };    等待QueryAsync(executeCommand,值);    返回foo的;
}公共异步任务QueryAsync(Func键<的SqlCommand,任务> executeCommand,int值)
{
    使用(SqlConnection的连接=新的SqlConnection(...))
    {
        connection.Open();        使用(的SqlCommand命令= connection.CreateCommand())
        {
            //设置查询...            等待executeCommand(命令);            //日志结果...            返回;
        }
    }
}


解决方案

而不是 Task.WaitAll 你需要使用的await Task.WhenAll

在ASP.NET你有一个实际的同步上下文。这意味着,毕竟等待要求你将被编组回这方面执行的延续(实际上这些序列延续)。在一个控制台应用程序没有同步的背景下,让所有的延续的只是发送到线程池。通过在请求的上下文中使用 Task.WaitAll 您阻止它,这是$ P $被用来从所有其他任务处理延续pventing吧。

另外请注意在ASP应用程序,异步的的主要优点之一/等待是的的阻止你使用来处理请求的线程池中的线程。如果使用 Task.WaitAll 你打败这一目的。

使得这种变化的一个副作用是,通过从阻塞操作移动到的await操作异常将被不同的传播。而不是抛出 AggregateException 它会抛出底层例外之一。

Below is a simplified version of the code I'm having trouble with. When I run this in a console application, it works as expected. All queries are run in parallel and Task.WaitAll() returns when they are all complete.

However, when this code runs in a web application, the request just hangs. When I attach a debugger and break all, it shows that execution is wait on Task.WaitAll(). And the first task has completed, but the others never finish.

I can't figure out why it hangs when running in ASP.NET, but works fine in a console application.

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;
        }
    }           
}

解决方案

Rather than Task.WaitAll you need to use await Task.WhenAll.

In ASP.NET you have an actual synchronization context. This means that after all await calls you will be marshaled back to that context to execute the continuation (effectively serializing these continuations). In a console app there is no synchronization context, so all of the continuations are just sent to the thread pool. By using Task.WaitAll in the request's context you're blocking it, which is preventing it from being used to handle the continuations from all of the other tasks.

Also note that one of the primary benefits of async/await in an ASP app is to not block the thread pool thread that you're using to handle the request. If you use a Task.WaitAll you're defeating that purpose.

A side effect of making this change is that by moving from a blocking operation to an await operation exceptions will be propagated differently. Rather than throwing AggregateException it will throw one of the underlying exceptions.

这篇关于Task.WaitAll挂在ASP.NET多个awaitable任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆