我想知道编译器在使用async关键字进行编译时选择TaskScheduler的方式背后的原因。
我的测试方法由OnConnectedAsync方法上的SignalR(ASP.NET主机,IIS8,websocket传输)调用。
protected override async Task OnConnectedAsync(IRequest request, string connectionId)
{
SendUpdates();
}
在Current同步上下文中启动任务将导致System.Web.AspNetSynchronizationContext.OperationStarted()中的InvalidOperationException
此时无法启动异步操作。异步操作只能在异步处理程序或模块中启动,或者在页面生命周期中的某些事件中启动。如果在执行页面时发生此异常,请确保将页面标记为
<%@ Page Async="true" %>
。
精细。使用此SendUpdates定义,我得到以上异常:
private async void SendUpdates()
{
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
await Connection.Broadcast("blabla");
}
});
}
但更有趣的是,当我没有得到例外。以下作品:
private void SendUpdates()
以下也是如此
private async Task SendUpdates()
这最后一个也有效,但它与上面的例子基本相同。
private Task SendUpdates()
{
return Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
await Connection.Broadcast("blabla");
}
});
}
您知道编译器如何选择在此使用哪个调度程序吗?
答案 0 :(得分:12)
编写async
代码的主要指导原则之一是“避免async void
” - 即使用async Task
代替async void
,除非您正在实施async
1}}事件处理程序。
async void
方法使用SynchronizationContext
的{{1}}和OperationStarted
;有关详细信息,请参阅我的MSDN文章It's All about the SynchronizationContext。
ASP.NET检测到对OperationCompleted
的调用并且(正确地)拒绝它,因为在那里放置OperationStarted
事件处理程序是非法的。当您更正代码以使用async
时,ASP.NET不再会看到async Task
事件处理程序。
您可能会发现我的intro to async
/ await
post有帮助。
答案 1 :(得分:3)
致电时:
private async void SendUpdates()
通过调用Task.Run
并使用匿名委托上的async
keyword,您实际上并未提供延续;你开始Task
,然后你给Run
方法一个延续,然后它处理。对于调用Task.Run
的代码,该延续不会被带回任何有意义的内容。
这就是为什么你得到异常的原因,处理程序不会<{>}知道Task
Task.Run
private void SendUpdates()
对async
的调用产生的原因。
那说:
Task
正常运行,因为任务已创建且代码未捕获await
(因为方法上没有private async Task SendUpdates()
关键字,Task
实例默认不捕获它)。你正在解雇这个任务,但这是一次又一次的忘记。
以下也有效:
SynchronizationContext
即因为在返回await
时,您已经返回了等待回调可以使用的内容。
要直接回答您的问题,编译器会确保在您致电SynchronizationContext
之前从SynchronizationContext
返回{{1}};在使用{{1}}