2017-11-12 16 views
5

async/awaitについてもう少し理解しています。特に、asyncメソッドとawaitでコンパイラが「一時停止」する方法を知っていると、追加のスレッドが生成されません。一例として、C#コンパイラは、非同期メソッドをいつ切断するかを知っていますか?

、私は私の方法は、「一時停止」を取得しますし、それを呼び出したスレッドは、スレッドプールへとTaskいったん戻り、await sqlConnection.OpenAsync();があることを知っているのは、私が

DoSomeStuff(); 
await sqlConnection.OpenAsync(); 
DoSomeOtherStuff(); 

ようasync方法を持っているとしましょうつまり、接続の開始を追跡すると、使用可能なスレッドがDoSomeOtherStuff()を実行していることがわかります。

| DoSomeStuff() | "start" opening connection | ------------------------------------ | 
| ---------------------------------------------------------- | DoSomeOtherStuff() - | 

ここで私は混乱するところです。私はOpenAsynchttps://referencesource.microsoft.com/#System.Data/System/Data/Common/DBConnection.cs,e9166ee1c5d11996,references)のソースコードを見て、それは私が仕事を外部リソースとの通信を開始しているため、コンパイラは、スレッドを「断つ」ことを知っているだろういくつかの場所を見て想像し

public Task OpenAsync() { 
     return OpenAsync(CancellationToken.None); 
    } 

    public virtual Task OpenAsync(CancellationToken cancellationToken) { 
     TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>(); 

     if (cancellationToken.IsCancellationRequested) { 
      taskCompletionSource.SetCanceled(); 
     } 
     else { 
      try { 
       Open(); 
       taskCompletionSource.SetResult(null); 
      } 
      catch (Exception e) { 
       taskCompletionSource.SetException(e); 
      } 
     } 

     return taskCompletionSource.Task; 
    } 

だが、私は実際にそれを見ていない、そして実際には、Open();はそれが同期的に待っていることを暗示しているようです。誰かがこれがスレッドレスの「真の非同期」コードになる方法を説明できますか?

+3

このようなOpenAsyncの実装は、実際には「真の非同期」コードではありません。 – CodeFuller

+0

C#コンパイラはコードを書き直すと、隠しクラスの* 2つのメソッドに変わります。 DoSomeOtherStuff()呼び出しは2番目のメソッドにあります。あなたが持っていて、両方の部分で使われているローカル変数は、そのクラスのフィールドになります。 ildasm.exeユーティリティを使って見ることができます。 –

+0

@CodeFuller OpenAsync()の "真の非同期"実装の例を教えてください。あるいは、上記の 'OpenAsync()'がどのように真のasyncになるのかを示すことができますか? – Questionaire

答えて

5

使用することの利点

await sqlConnection.OpenAsync(); 

次を呼び出したスレッドが解放されるだろうし、属しているスレッドプールからによって利用されるために利用可能になることから、結果を非同期では、待っています。 ASP.NETアプリケーションについて話している場合、スレッドは解放され、別の着信HTTP要求を処理するために使用できます。この方法では、ASP.NETスレッドプールのスレッドはHTTPリクエストを処理するために常に使用でき、データベースへの接続を開いてSQL文を実行するなど、I/Oのためにブロックしません。

更新

あなたがawaitしようとしているタスクが完了している場合、あなたのコードは、同期的に実行することをここに明記しなければなりません。

+0

バックグラウンドスレッドで実行するのに 'Open()'は必要ないので、2番目の段落、つまりOpen()_は誤解を招きます。示された実装は同期的であり、それ自体ではコンテキストを変更しません。 'OpenAsync'と' Open'は同じコンテキストで動作し、呼び出しに関連する単一スレッドをブロックします。 – JSteward

+0

@JSteward私はあなたの議論が正しいとは確信していません。 https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/await ***表現が待たれても、実行中のスレッドはブロックされません**。代わりに、コンパイラは待機しているタスクの続きとしてasyncメソッドの残りの部分を登録します。制御はasyncメソッドの呼び出し元に戻ります。タスクが完了すると、その継続が呼び出され、非同期メソッドの実行が中断したところから再開されます。*待っているタスクのコード全体が別のスレッドで実行されます。 – Christos

+0

この動作は、 'Task/Task 'メソッドの実装がこれまでに真の非同期メソッドを生成して表す場合にのみ適用されます。実装が同期でブロックされている場合。 'await'は自動的に作業を別のコンテキストに移動しません。ブロッキング実装は、待つことができる戻り値の型にかかわらず常にブロックされます。 – JSteward

6

あなたの方法は、必ずしも待っているとは限りません。待っているタスクがすでに完了している場合(提供したコードの場合) - メソッドは通常どおり続行されます。 DbConnectionが基本クラスであり、メソッドOpenAsyncが仮想なので、あなたが見ている方法はSqlConnectionで実際に使用されている方法ではありません。 SqlConnectionはそれをオーバーライドし、実際の非同期実装を提供します。しかし、すべてのプロバイダがそうしているわけではなく、あなたの質問に示す実装を本当に使用しない人もいます。

このような実装を使用すると、すべてのスレッドが同期して実行されます。あなたは

public async Task Do() { 
    DoSomeStuff(); 
    await sqlConnection.OpenAsync(); 
    DoSomeOtherStuff(); 
} 

があり、OpenAsyncの本当の非同期バージョンを提供していないプロバイダを使用すると仮定します。その後、誰かがawait Do()を呼び出すと、すべての作業(DoSomeStuffOpenAsyncDoSomeOtherStuff)が実行されます。これがUIスレッドの場合、UIスレッド内でこのようなプロバイダに対して「非同期」メソッドを使用すると、そのような状況が頻繁に発生します。

+0

OpenAsync()の「真の非同期」実装の例を教えてください。 – Questionaire

+0

ここで@QuestionaireはSqlConnectionのソースコードです。http://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlConnection.cs – Evk

+0

@Questionaire real asyncはほとんどが非同期IO(ファイル、ソケット)です。あなたはこれを読んで興味があるかもしれません:https://blog.stephencleary.com/2013/11/there-is-no-thread.html – Evk

関連する問題