2016-02-22 11 views
5

クラスの非同期メソッドを実装する際に一般的に、私はこのような何かを書く:Task.Runを悪い方法で使用していますか?

public Task<Guid> GetMyObjectIdAsync(string objectName) 
{ 
    return Task.Run(() => GetMyObjectId(objectName)); 
} 

private Guid GetMyObjectId(string objectName) 
{ 
    using (var unitOfWork = _myUnitOfWorkFactory.CreateUnitOfWork()) 
    { 
     var myObject = unitOfWork.MyObjects.Single(o => o.Name == objectName); 
     return myObject.Id; 
    } 
} 

パターンのこの種のは、私が(状況に応じて、同期および非同期で私の仕事のほとんどを同じロジックを使用することができます私は同期メソッドを公開し、必要に応じて最大限の互換性を得ることができるので、まだ非同期呼び出しをサポートしているわけではありません)。

最近、Task.Run()を使用することが推奨されるいくつかの記事を読んだことが悪い考えであり、特定の状況でのみ使用する必要がありますが、そのような状況はあまり明確ではありません。

上記のパターンは実際には悪い考えですか?このようにして非同期呼び出しの機能/意図した目的のいくつかを失っていますか?それとも正当な実装ですか?

+1

あなたはその神秘的なSOの投稿をリンクしてみませんか?私は彼らが既にあなたの答えを含んでいるべきである、という意味ですか? – nvoigt

+0

基本的には、「あなたはTask.Run()を使い捨てコメントの一種として使用してはいけません」と言い続けていますが、間違っているかもしれないと心配しています。 –

+1

https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Async-Library-Methods-Shouldn-t-Lie – SLaks

答えて

10

あなたがやっていることは、別のスレッドに同期操作をオフロードすることです。あなたのスレッドが "特別な"場合は、それは完璧です。 「特別な」スレッドの一例は、UIスレッドである。その場合は、UIを応答的に保つために、作業をオフロードすることができます(別の例は何らかのリスナーです)。

しかし、ほとんどの場合、あるスレッドから別のスレッドに移動するだけです。これは値を追加せず、不必要なオーバーヘッドを追加します。

ので:

は、私が実際に悪い考えの上に描かれてきた模様ですか?

はい、そうです。同期作業をThreadPoolに委託し、あたかも非同期であるかのように見せかけるのは悪い考えです。

私はこのように非同期呼び出しの機能性/意図した目的を失っていますか?

この操作については、実際には何も起きていません。あなたは、リモートマシン上でこれを実行すると、あなたはそれをやってから利益を得ることができた場合は、非同期操作自体は意味、本当に非同期する必要があります:

var myObject = await unitOfWork.MyObjects.SingleAsync(o => o.Name == objectName); 

あなたが現在「同期オーバー非同期」とおそらくあなたと呼ばれてやっていますそれをしてはならない。私はこれを見たときShould I expose asynchronous wrappers for synchronous methods?

+0

「非同期コード」と「スレッドコード上で実行される同期コード」の違いは何ですか?それはカメのすべての方法ではないですか? – CodeCaster

+0

私は正しく理解していれば、これを非同期に実行することはできません。非同期フレームワークに収まるようにするだけです。それは正確な陳述ですか? –

+0

@JeremyHolovacsあなたは 'タスク'を作成しています。使用しているフレームワークはTPL(Task Parallel Library)です。それは非同期待ちの前に来て、「その下に生きる」。あなたは非同期に隣接していますが、ここでは真に非同期なものは何もありません。 – i3arnon

3
public Task<Guid> GetMyObjectIdAsync(string objectName) 

でより多くの、私はこの方法を使用するだけではなくTask.Run()で自分自身をそれをラップでいくつかの利点があると期待しています。

特に、I/Oがヒットするとスレッドを解放するか、そうしないとスレッドを解放することができます。今

私は、コードを持っている場合は考慮してください。

_resource = GetResourceForID(GetMyObjectIdAsync(SomeLongRunningWayToGetName())); 

(私はこれはタスクで行われている必要があり理由を持っている、と私はTask.Run()が実際に意味を持たない状況の一種でよ場合私のためにここで

Task task = Task.Run(() => _resource = GetResourceForID(GetMyObjectIdAsync(SomeLongRunningWayToGetName()))); 

Task.Run()悪い考え次のようになります。私は)別のスレッド上に全体をラップするだろうこれを行うための最善の方法を、それをオフロードする理由があります電話番号、またはそれは私が実際に私に与えるものから得ているので、良いかもしれません。

あなたの署名が表示されたら、と考えると、コードでこれを行う最良の方法は、そのメソッドを使用するコードに変換することです。 (asyncawaitを使用してまたは類似の)

Task task = SomeLongRunningWayToGetName() 
    .ContinueWith(t => GetMyObjectIdAsync(t.Result)) 
    .ContinueWith(t => _resource = GetResourceForIDAsync(t.Result)); 

これはせいぜい、チャンク化があまり良くありません。Task.Run()です。悪い場合、私はawaitです。実際にはそれが利用可能だったコンテキストでは提供しないより優れた非同期性を得るためにこれを行うだけです。 (これは、私が非同期にしたMVCアクションでこれを使用したのは、余分なオーバーヘッドがより良いスレッドプールの使用で返済されると考えたからです)。

したがって、Task.Run()が有用な場合がありますが、この場合は常に悪いです。私がクラスの使用にもたらすことができるよりも大きな非同期性を私に与えることができないなら、私があなたのことを信じるように導かないでください。

非同期I/Oを実際に呼び出す場合は、公開XXXAsync()メソッドのみを提供してください。

には、非同期メソッドをスタブアウトするために、実際にが必要です。これは悪い、あまりにも(呼び出し側がまだちょうど直接GetMyObjectId()を呼び出すほうだったでしょう)が、少なくともコードawait sの場合である

public Task<Guid> GetMyObjectIdAsync(string objectName) 
{ 
    return Task.FromResult(GetMyObjectId(objectName); 
} 

:のように良いだろう、共有ベースまたはインタフェースの署名と一致同じスレッド上で動作している間に、別のスレッドを使用してオーバーヘッドが発生しないので、他のスレッドと混ざり合っていると、悪影響が軽減されます。したがって、の場合、Taskを返す必要がありますが、それを呼び出す方法には何も役立ちません。

本当にが必要な場合は、それを提供する必要はありません。

(プライベートメソッドRun()を呼び出すすべてのコールサイトのメリットが異なるため、いくつかの場所でRun()を呼び出すのではなく、便利性を追加するだけですが、十分に文書化する必要があります)。

9

最近私はTask.Run()を使用することが悪い考えであり、特定の状況下でのみ使用すべきであることを示唆するいくつかのSOの投稿を読んだことがあります。

親指の絶対に裸の骨のルール私は非同期に新しく追加された人々に伝えるには、次のとおりです。

まず、目的を理解します。 非同期は、高遅延動作の重要な非効率を軽減するためのものです。

あなたは低レイテンシーをしていますか?それでは、非同期にしないでください。ちょうど仕事をしてください。これは速い。低遅延のタスクで遅延を緩和するツールを使用するだけで、プログラムが不必要に複雑になるだけです。

スピンするディスクやパケットが表示されるのを待っているため、待ち時間が長くなっていますか?これを非同期にしますが、は別のスレッドに入れないでください。手紙を受け取るのを待っているあなたの郵便受けに座る労働者を雇うことはありません。郵便システムはすでにあなたに非同期で実行されています。より非同期にするために人を雇う必要はありません。それが明確でない場合は、「スレッドはありません」と読んでください。

巨大な計算を行うためにCPUで待機するレイテンシの高い作業はありますか? 10ミリ秒以上かかる計算のように?その後、スレッドをアイドル状態のCPUにスケジューリングできるように、そのタスクをスレッドにオフロードします。

+0

あなたのコメントから、この非同期を作成しますが、別のスレッドに配置しないでください。私は、上記のスニペットは正反対のものですか?どのように私はそれを非同期的にするだろうが、同じスレッドでそれを維持する例を提供できますか? –

+0

@JeremyHolovacs:(1)高レイテンシ操作が開始された時点で呼び出し元に制御を戻し、(2)操作が完了したときに実行する必要がある作業を覚えていること、 *継続* - (3)操作が完了したときを検出し、継続を実行するようスケジュールする。操作が完了したときをどのように知っているかは、操作に依存します。あなたは、あなたの操作が完了したときよりも良く分かるでしょう。 –

+0

@JeremyHolovacs:あなたの特別なケースでは、私が遅いことがあなたが非同期にしようとしていることは分かりません。作業単位の作成、オブジェクトセットのフェッチ、名前のフェッチ、オブジェクトセットのフィルタリング、または識別子の生成は30ミリ秒以上かかる可能性がありますか?それがどれであるかは、非同期化に集中しなければならないことです。 –

関連する問題