作者:平凡咸伟 | 来源:互联网 | 2023-10-10 03:02
async关键字和TaskScheduler的选择我想知道编译器在使用async关键字进行编译时选择TaskScheduler的方式背后的原因。我的测试方法由OnConnected
async关键字和TaskScheduler的选择
我想知道编译器在使用async关键字进行编译时选择TaskScheduler的方式背后的原因。
我的测试方法由OnConnectedAsync方法上的SignalR(ASP.NET主机,IIS8,websocket传输)调用。
protected override async Task OnConnectedAsync(IRequest request, string connectionId) { SendUpdates(); }
在Current同步上下文中启动任务将导致System.Web.AspNetSynchronizationContext.OperationStarted()中的InvalidOperationException
此时无法启动异步操作。 异步操作只能在异步处理程序或模块中启动,或者在页面生命周期中的某些事件中启动。 如果在执行页面时发生此exception,请确保将页面标记为
。
精细。 使用此SendUpdates定义,我得到以上exception:
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"); } }); }
你知道编译器如何选择在这里使用哪个调度程序吗?
编写async
代码的主要原则之一是“避免async void
” – 也就是说,除非您正在实现async
事件处理程序,否则请使用async Task
而不是async void
。
async void
方法使用SynchronizationContext
的OperationStarted
和OperationCompleted
; 请参阅我的MSDN文章。有关详细信息,请参阅SynchronizationContext 。
ASP.NET检测到对OperationStarted
的调用并且(正确地)拒绝它,因为在那里放置async
事件处理程序是非法的。 当您更正代码以使用async Task
,ASP.NET不再看到async
事件处理程序。
你可能会发现我的async
/ await
post介绍很有帮助。
你打电话的时候:
private async void SendUpdates()
通过调用Task.Run
并在匿名委托上使用async
关键字 ,您实际上并没有提供延续; 你启动Task
,然后你给Run
方法一个延续,然后它处理。 对于调用Task.Run
的代码,这种延续不会被带回任何有意义的Task.Run
。
这就是为什么你得到exception,处理程序不知道在Task
上await
对Task.Run
的调用产生。
那说:
private void SendUpdates()
之所以有效,是因为创建了任务并且代码没有捕获SynchronizationContext
(因为方法上没有async
关键字, Task
实例默认不捕获它)。 你正在解雇这个任务,但这是一场不容易的事。
以下也是如此:
private async Task SendUpdates()
也就是因为在返回Task
,你已经返回了一个等待回调可以使用的等待。
要直接回答您的问题,编译器将确保在调用await
之前从SynchronizationContext.Current
返回SynchronizationContext.Current
; 在使用SynchronizationContext
调用等待返回之后调用的任何延续。
上述就是C#学习教程:async关键字和TaskScheduler的选择分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—编程笔记