web-dev-qa-db-ja.com

Task.Factory.StartNew vs Task.Factory.FromAsync

I/Oバウンドメソッド(DB呼び出しを行うメソッドなど)があるとします。このメソッドは、同期および非同期の両方で実行できます。あれは、

  1. 同期:

    IOMethod()
    
  2. 非同期:

    BeginIOMethod()
    EndIOMethod()
    

次に、以下に示すようにさまざまな方法でメソッドを実行すると、リソース使用率に関するパフォーマンスの違いは何ですか?

  1. var task = Task.Factory.StartNew(() => { IOMethod(); });
    task.Wait();
    
  2. var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
    task.Wait();
    
41
soleiljy
_var task = Task.Factory.StartNew(() => { IOMethod(); });
task.Wait();
_

これにより、IOMethod()の実行中にスレッドプールスレッドがブロックされ、Wait()により現在のスレッドもブロックされます。ブロックされたスレッドの合計:2。

_var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.Wait();
_

これは(ほとんどの場合)スレッドを使用せずに非同期的に操作を実行しますが、Wait()のために現在のスレッドをブロックします。ブロックされたスレッドの合計:1。

_IOMethod();
_

これは、IOMethod()の実行中に現在のスレッドをブロックします。ブロックされたスレッドの合計:1。

現在のスレッドをブロックする必要がある場合、またはブロックしても問題ない場合は、TPLを使用しても実際には何も得られないため、これを使用する必要があります。

_var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
await task;
_

これにより、スレッドを使用せずに非同期で操作が実行されます。また、awaitのおかげで、操作が非同期的に完了するまで待機します。ブロックされたスレッドの合計:0。

これは、非同期性を利用したい場合に使用すべきものであり、C#5.0を使用できます。

_var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.ContinueWith(() => /* rest of the method here */);
_

これにより、スレッドを使用せずに非同期で操作が実行されます。また、ContinueWith()のおかげで、操作が非同期的に完了するまで待機します。ブロックされたスレッドの合計:0。

これは、非同期性を利用したいがC#5.0を使用できない場合に使用すべきものです。

65
svick

(1).NETスレッドプールが(おそらく)Taskを処理します。

(2)BeginIOMethod/EndIOMethodペアがネイティブに使用するメカニズムを使用して、非同期部分を処理します。非同期部分は.NETスレッドプールを含む場合と含まない場合があります。

たとえば、BeginIOMethodがインターネット経由でTCPメッセージを送信しており、後で受信者が応答でTCPメッセージを送信する場合(受信した場合) EndIOMethod)によって、操作の非同期的な性質は.NETスレッドプールによって提供されません。使用されているTCPライブラリは、非同期部分を提供しています。

これは TaskCompletionSource class を使用して実現できます。 _Task.Factory.FromAsync_は_TaskCompletionSource<T>_を作成し、その_Task<T>_を返し、EndIOMethodをトリガーとして使用して、返された_Task<T>_にResultを配置できます。呼び出し時のフォーム_Task.Factory.FromAsync_.

リソース使用率に関するパフォーマンスの違いは何ですか?

(1)と(2)の違いは、主に.NETスレッドプールにワークロードを追加するかどうかだけです。一般に、行うべき正しいことは、_Task.Factory.FromAsync_/_Begin..._のペアしか持っていない場合は_End..._を選択し、それ以外の場合は_Task.Factory.StartNew_を選択することです。


C#5.0を使用している場合は、task.Wait();の代わりに非ブロッキング_await task;_を使用する必要があります。 (svickの回答を参照してください。)

1
Timothy Shields