异步关键字和的TaskScheduler的选择(async keyword and choice o

2019-07-02 09:31发布

我想知道使用async关键字编译时,编译器选择的TaskScheduler的方式背后的原因。

我的测试方法是通过SignalR(ASP.NET主机,IIS8,WebSocket的运输)的OnConnectedAsync方法调用。

protected override async Task OnConnectedAsync(IRequest request, string connectionId)
{
   SendUpdates();
}

启动对当前同步上下文任务将导致在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");
                }
            });

    }

你知道编译器如何选择在这里使用的调度?

Answer 1:

一个书面的首要准则async代码为“避免async void ” -也就是说,使用async Task ,而不是async void ,除非你实现一个async事件处理程序。

async void的方法使用SynchronizationContextOperationStartedOperationCompleted ; 看到我的MSDN文章这是所有关于的SynchronizationContext的更多细节。

ASP.NET检测调用OperationStarted和(正确地)拒绝它,因为它是非法的把一个async事件处理程序存在。 当你正确使用代码async Task ,然后ASP.NET不再看到一个async事件处理程序。

您可能会发现我的介绍到async / await张贴有帮助。



Answer 2:

你打电话的时候:

private async void SendUpdates()

随着呼叫Task.Run和使用async关键字的匿名委托,你实际上并没有提供一个延续; 启动Task ,以及你给的Run方法的延续,它再处理。 这延续是不以任何有意义的引导回调用代码Task.Run

这就是为什么你得到的异常,处理程序不知道 awaitTask在调用Task.Run产生。

这就是说:

private void SendUpdates()

由于工作在创建任务和代码不会捕获SynchronizationContext (因为没有async的方法关键字, Task情况下默认是不捕捉到它)。 你是射击任务,但它的发射后不管。

而下面的工作太:

private async Task SendUpdates()

即因为在返回的Task ,你已经返回awaitable回调可以工作。

要直接回答你的问题,编译器将确保得到SynchronizationContext从返回SynchronizationContext.Current打电话之前await ; 后awaitable回报将使用被称为无论延续叫SynchronizationContext



文章来源: async keyword and choice of the TaskScheduler