下面是我遇到问题的代码的简化版本.当我在控制台应用程序中运行它时,它按预期工作.所有查询都是并行运行的,并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); …
Run Code Online (Sandbox Code Playgroud)
AspNetSynchronizationContext
是最奇怪的实现.它将处理Post
为同步而非异步,并使用锁定一次执行一个委托.
同样,他在同步上下文中写的文章与该评论中的链接表明:
从概念上讲,AspNetSynchronizationContext的上下文很复杂.在异步页面的生命周期中,上下文仅从ASP.NET线程池中的一个线程开始.异步请求启动后,上下文不包含任何线程.当异步请求完成时,执行其完成例程的线程池线程进入上下文.这些线程可能与发起请求的线程相同,但更可能是在操作完成时任何线程都是空闲的.
如果同一应用程序同时完成多个操作,AspNetSynchronizationContext将确保它们一次执行一个.它们可以在任何线程上执行,但该线程将具有原始页面的标识和文化.
挖掘反射器似乎验证了这一点,因为它HttpApplication
在调用任何回调时需要锁定.
锁定app对象看起来像是可怕的东西.所以我的第一个问题:这是否意味着今天,整个应用程序的所有异步完成都会一次执行一个,即使是源自具有单独HttpContexts的单独线程上的单独请求的那些?对于任何100%使用异步页面(或MVC中的异步控制器)的应用程序来说,这不是一个巨大的瓶颈吗?如果没有,为什么不呢?我错过了什么?
此外,在.NET 4.5中,它看起来像是一个新的AspNetSynchronizationContext
,旧的重命名LegacyAspNetSynchronizationContext
,仅在UseTaskFriendlySynchronizationContext
未设置新的应用程序设置时使用.问题2:新实现是否会改变这种行为?否则,我想通过同步上下文新的async/await支持编组完成,这种瓶颈会更频繁地被注意到.
这个论坛帖子的答案(从SO答案链接到这里)表明这里有一些根本改变的东西,但我想明确这是什么以及改进了什么行为,因为我们有一个.NET 4 MVC 3应用程序100%异步操作方法进行Web服务调用.
我StackExchange.Redis
在我的Nancy应用程序中使用(SE.R).有一个全局ConnectionMultiplexer
由Nancy TinyIoC
通过构造函数参数自动传递,任何时候我尝试使用GetDatabase
其中一个*Async
方法(同步方法只在尝试了一个异步方法后才开始失败)我的应用程序死锁.
看看我的并行堆栈,看起来我有四个线程:
Result
我的一个使用SE.R的任务的线程.(在堆栈上有很多Nancy东西,然后调用我的库使用SE.R,并调用Result
.堆栈顶部是Monitor.Wait
).Native to Managed Transition
,ThreadHelper.ThreadStart
在堆栈的顶部是ThreadHelper.ThreadStart_Context
.Monitor.Wait
Monitor.Wait
SocketManager.WriteAllQueues
SocketManager.cctor.AnonymousMethod__16
Managed to Native Transition
SocketManager.ReadImpl
SocketManager.Read
SocketManager.cctor.AnonymousMethod__19
我几乎可以肯定这是某种僵局.我甚至认为这可能与这个问题有关.但我不知道该怎么做.
该ConnectionMultiplexer
设置在南希IRegistrations
用下面的代码:
var configOpts = new ConfigurationOptions {
EndPoints = {
RedisHost,
},
Password = RedisPass,
AllowAdmin = false,
ClientName = ApplicationName,
ConnectTimeout = 10000,
SyncTimeout = 5000,
};
var mux = …
Run Code Online (Sandbox Code Playgroud)