2016-01-20 18 views
5

私は大まかな草案のために、以下のパターンを使用して大量のデータアクセスメソッドを非同期に変換しました。後の繰り返しではそれほど簡単ではありません。それはどれくらい安全で、何が欠けているのですか、どうすればいいでしょうか?これは単純な非同期呼び出しを非同期にしていますか?

実行時間の長い通話を提供するサービス:

private class UserService 
{ 
    public IdentityUser GetById(int id) 
    { 
     ... 
    } 
} 

private UserService _userService = new UserService(); 

元の同期方法:

public IdentityUser GetById(int id) 
{ 
    return _userService.GetById(id); 
} 

私の素晴らしい新しい非同期メソッド:

public async Task<IdentityUser> GetByIdAsync(int id) 
{ 
    await Task.Run(() => _userService.GetById(id)); 
} 
+0

長時間実行されるクエリには、対処する必要のある可能性のある問題のリストが多数あります。なぜSignalR Nugetをインストールしてそれらをすべてあなたのために修正するのではなく、WebSocketのサポートも! –

+0

@MattiasÅslund:異なる人々は、「長期実行」の定義が異なります。私は、GetById()がSignalRの使用に適しているような、「長期実行」のようなものではないと感じています。 – StriplingWarrior

+0

あなたはWCFサービスまたはWeb APIとして消費していますか?そうであれば、サーバ側では悪化するだけです。 – Noseratio

答えて

5

あなたは「作るべきではありません偽の非同期 "のようなメソッド:

public async Task<IdentityUser> GetByIdAsync(int id) 
{ 
    await Task.Run(() => _userService.GetById(id)); 
} 

私が「偽の非同期」と呼ぶ理由は、動作に関して本質的に非同期なものがないからです。この場合、同期メソッドのみが必要です。呼び出し元がTask.Runを使用して非同期にしたい場合、呼び出し側はそれを行うことができます。

何かが本質的に非同期ですか?たとえば、要求を送信してから応答を受け取るまでの間に、Webサービスまたはデータベースに要求を行うと、待ち時間があります。要求は本質的に非同期操作です。呼び出し元のスレッドをブロックしないようにするには、async-awaitを使用します。動作

+0

を参照してください。メソッドを実装する必要があります私の内部操作を偽の非同期にしないとコンパイルされず、 'UserService'は非同期メソッドを提供しないので、' public async Task GetByIdAsync(int id) 'とまったく同じです。 – ProfK

+2

@ProfKを返し、 'Task.FromResult'を返し、' async'キーワードを付けないで実装します(これは、とにかくコンパイルされたメソッドシグネチャの一部ではありません)。 – Noseratio

+1

@Noseratioもし 'Task.FromResult'のようなものであれば、私は' Task'や 'await'を返さなければならなかった多くの場所を使っています。私はそれを安全にプレイし、私の「偽物」がすべてそれを使用するかもしれないと思う。 – ProfK

2

技術は、それ自体がラップと本質非同期操作にブロックされて同期操作を実行するために新しいスレッドを作成することによって動作します。つまり、最初にasyncに行くという最大のメリットが得られないということです。

適切な方法は、すべての方法で非同期にすることです。

private class UserService 
{ 
    public async Task<IdentityUser> GetByIdAsync(int id) 
    { 
     return await mContext.Users.SingleAsync(u => u.Id == id); 
    } 
} 

使用法:

public async Task<IdentityUser> GetByIdAsync(int id) 
{ 
    return await _userService.GetByIdAsync(id); 
} 

想定し、もちろん、その

private class UserService 
{ 
    public IdentityUser GetById(int id) 
    { 
     return mContext.Users.Single(u => u.Id == id); 
    } 
} 

...あなたは今、非同期バージョンを作成する必要があります。今のに対し、あなたは、おそらくこのようなものを持っています基本的なフレームワークは、本質的に非同期操作のためのSingleAsync()のような非同期メソッドをサポートしています。これにより、データベースを待つ間に現在のスレッドを解放することができます完了するための操作。スレッドは他の場所で再利用することができ、操作が完了すると、その時点で使用可能なスレッドがあれば使用できます。

おそらく約these Best Practicesを読んで採用する価値があります。たとえば、セッションやリクエストなどのコンテキスト情報にアクセスしていない場所では、.ConfigureAwait(false)を使用することをお勧めします。

この回答はもちろん、GetByIdが本質的に非同期であることを前提としています。つまり、ハードドライブやネットワーク上の場所などから取得しています。長時間実行されているCPU操作を使用してユーザーのIDを計算する場合は、Task.Run()を使用することをお勧めします。また、引数がTask.Run()に長時間実行されていることをさらに指定する必要があります。

+0

私は 'id'を計算していません。特定のIDを持つDBのユーザを探しています。おそらくDBには何百万というユーザがいて、' Id 'にインデックスはありません。 – ProfK

+0

@ProfK:データベースのバックエンドなぜ非同期タスクをサポートしていないのですか?最初に 'GetByIdAsync'メソッドを' async'メソッドにしようとしていますか?この呼び出しが実行されている間に呼び出し元が別のタスクに移動できるようにするには、呼び出し側が 'Task.Run()'を呼び出す必要があります。 (http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.htmlを参照してください)これはいつか非同期になることを期待して、コードを将来的に検証するだけですか?その場合、 'return Task.FromResult(_userService.GetById(id));'を返します。さもなければ、 'async'を全く使用する理由がないので、そうしないでください。 – StriplingWarrior

0

Task.Run()は、CPUバインド作業にのみ使用する必要があります。しかし、なぜか覚えていない。 最終的に非同期リソースを呼び出すGetByIdAsync()メソッドを作成してください。

+0

私はまだ非同期をサポートしていないことが分かっている限り、非同期リソースを持たず、リソースはNHibernateです。 – ProfK

関連する問題