8

コンパイラがasyncキーワードを使用してコンパイルするときにTaskSchedulerを選択する方法の背後にある理由を知りたいです。

私のテストメソッドは、OnConnectedAsyncメソッドのSignalR(ASP.NETホスト、IIS8、WebSocketトランスポート)によって呼び出されます。

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

    }

コンパイラがここで使用するスケジューラをどのように選択するか知っていますか?

4

2 に答える 2

12

asyncコードを記述する際の主なガイドラインの 1 つは、 「避ける」ことです。つまり、イベント ハンドラーを実装する場合を除き、代わりに をasync void使用します。async Taskasync voidasync

async voidメソッドは と を使用しSynchronizationContextます。詳細については、MSDN の記事It's All about the SynchronizationContextを参照してください。OperationStartedOperationCompleted

ASP.NET は呼び出しを検出しOperationStarted、(正しく) 拒否します。これは、asyncそこにイベント ハンドラーを配置することは違法であるためです。を使用するようにコードを修正するasync Taskと、ASP.NET はasyncイベント ハンドラーを認識しなくなります。

/投稿の紹介がasyncawait役立つ場合があります。

于 2012-10-24T05:35:52.610 に答える
3

電話すると:

private async void SendUpdates()

匿名デリゲートでキーワードを呼び出してTask.Run使用すると、実際には継続が提供されません。を開始すると、メソッドに継続が与えられ、それが処理されます。その継続は、 を呼び出したコードに意味のある形で戻されません。asyncTaskRunTask.Run

これが例外を取得する理由です。ハンドラーは、への呼び出しが生成することを認識awaitていません。TaskTask.Run

それは言った:

private void SendUpdates()

タスクが作成され、コードが a をキャプチャしないため機能します (メソッドにキーワードSynchronizationContextがないため、インスタンスはデフォルトでそれをキャプチャしません)。あなたはタスクを起動していますが、それは起動して忘れています。asyncTask

また、以下も機能します。

private async Task SendUpdates()

つまり、 を返す際Taskに、コールバックが使用できる awaitable を返したからです。

あなたの質問に直接答えるために、コンパイラはあなたが呼び出す前にSynchronizationContext返されたを確実に取得します; 待機可能なリターンの後に呼び出される継続は、それを使用して呼び出されます。SynchronizationContext.CurrentawaitSynchronizationContext

于 2012-10-23T13:40:47.427 に答える