2012-10-23 16 views
7

asyncキーワードを使用してコンパイルするときに、コンパイラがTaskSchedulerを選択する根拠を知りたいと思います。asyncキーワードとTaskSchedulerの選択

私のテストメソッドは、OnConnectedAsyncメソッドでSignalR(ASP.NETホスト、IIS8、websocketトランスポート)によって呼び出されます。現在の同期コンテキストでタスクを起動する

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

非同期操作は、この時点で開始することはできませんSystem.Web.AspNetSynchronizationContext.OperationStartedでInvalidOperationExceptionが()

になります。非同期操作は、非同期ハンドラまたはモジュール内、またはページライフサイクルの特定のイベント中にのみ開始できます。ページの実行中にこの例外が発生した場合は、Pageが<%@ Page Async="true" %>とマークされていることを確認してください。

Fine。この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"); 
       } 
      }); 

    } 

コンパイラがここで使用するスケジューラを選択する方法を知っていますか?お問い合わせの際

答えて

10

asyncコードを書くの主な指針の一つが「async voidを避けること」です。

async voidの方法は、SynchronizationContextOperationStartedOperationCompletedを使用します。詳細については、私のMSDN記事It's All about the SynchronizationContextを参照してください。

ASP.NETはOperationStartedへの呼び出しを検出し、そこで正しくasyncイベントハンドラを配置することができないため、(正しく)拒否します。 async Taskを使用するようにコードを修正すると、ASP.NETにはasyncイベントハンドラが表示されなくなります。

私のintro to async/await postが役に立ちます。

+0

これは答えです、ありがとう – Xin

3

Task.Runへの呼び出しで

private async void SendUpdates() 

を匿名デリゲートにasync keywordを使用して、あなたが実際に継続を提供していません。 Taskを開始し、Runメソッドに継続を与えてから処理します。その継続は、Task.Runと呼ばれるコードにとって意味のあるものではありません。

これは、あなたが例外を取得する理由は、ハンドラがTask.Runへの呼び出しが生成するTaskawaitを知らないです。

private void SendUpdates() 

作品をタスクは(方法はasyncキーワードが存在しないため、Taskインスタンスは、デフォルトでそれをキャプチャしていない)が作成され、コードがSynchronizationContextをキャプチャしていないので:言っ

。あなたはその仕事を解雇していますが、それは火事です。

そして次は、あまりにも動作します:

private async Task SendUpdates() 

Taskを返すには、あなたはコールバックが操作できるというawaitableを返されました。すなわちので。

awaitに電話する前に、コンパイラはSynchronizationContext.CurrentからSynchronizationContextが返されることを確認します。どのような継続が呼び出されても、それはSynchronizationContextを使用して待つことができます。あなたはasyncイベントハンドラを実装していない限り、つまり、代わりにasync voidasync Taskを使用 -

+0

実際、私は何かを待つことを望んでいません。あなたは非同期タスクSendUpdates()を書くと、私の内部タスクはASP.NET同期コンテキストと互換性のあるタスクにラップされますが、非同期void SendUpdates()を呼び出すと、このラッピングは起こらないことを意味しますか?問題? – Eilistraee

+0

@Eilistraee 'async Task SendUpdates'を書くと、' SynchronizationContext'が捕捉され、返された 'Task'が待たれます。 'async void SendUpdates'があるとき、あなたは何かを待っているわけではありません。何かがあれば、少なくともasyncを使っていて、対応する 'await'を持っていないことを示すコンパイラの警告が必要です(' await'はメソッドの実際の呼び出しコードではなく、匿名デリゲートにあります) )。 – casperOne

+0

しかし、非同期タスクSendUpdates()の場合でも、返されたタスクを待っていません。つまり、このタスク参照は失われ、OnConnectedAsyncは何も待たずに実行を継続します(内部タスクが完了しないために良いことです)。 – Eilistraee

関連する問題