2012-04-23 25 views
50

TPL &の違いを理解しようとすると、スレッド作成に関するasync/awaitが発生します。TPLとasync/await(スレッド処理)の違い

TPL(TaskFactory.Startnew)は、ThreadPool.QueueUserWorkItemと同様に動作し、スレッドプール内のスレッドに作業をキューイングすると考えています。これはもちろん、新しいスレッドを作成するTaskCreationOptions.LongRunningを使用しない限りです。私は非同期/のawait考え

ので、本質的に同じように動作します:

TPL:

Factory.StartNew(() => DoSomeAsyncWork()) 
.ContinueWith( 
    (antecedent) => { 
     DoSomeWorkAfter(); 
    },TaskScheduler.FromCurrentSynchronizationContext()); 

非同期/待つ:

await DoSomeAsyncWork(); 
DoSomeWorkAfter(); 

は同一です。私が読んでいたことから、非同期/唯一の「時々」が新しいスレッドを作成するのを待っているようです。では、いつ新しいスレッドを作成し、新しいスレッドを作成しないのですか? IO完了ポートを扱っていたら、新しいスレッドを作成する必要がないことがわかりますが、それ以外の場合はそれが必要であると思います。私は、FromCurrentSynchronizationContextについての私の理解は、常に少しでもあいまいだったと思います。私は本当に、本質的に、UIスレッドでした。

+2

実際、TaskCreationOptions.LongRunningは「新しいスレッド」を保証するものではありません。 MSDNでは、* "LongRunning"オプションはスケジューラにヒントを提供します。 *専用スレッドを保証するものではありません。 – eduncan911

+0

@ eduncan911あなたが書いていることは間違いありませんが、私はTPLのソースコードをしばらく前に調べました。実際には、 'TaskCreationOptions.LongRunning'が指定されたときに常に新しい専用スレッドが作成されています。 –

+0

@ZaidMasud:もう一度見たいかもしれません。'Thread.CurrentThread.IsThreadPoolThread'が数百ミリ秒の短期間のスレッドでtrueを返すため、スレッドをプールしていたことが分かります。私が複数のスレッドに出血を使用していたThreadStatic変数はもちろん、すべての種類の怪物を引き起こしていました。私は専用のスレッドを保証するために、自分のコードを古いスレッドの方法である複数のThread()を新しくするように強制しなければなりませんでした。言い換えれば、私はTaskFactoryを専用のスレッドに使用できませんでした。必要に応じて、専用スレッドを常に返す独自の 'TaskScheduler'を実装することもできます。 – eduncan911

答えて

64
私はTPL(TaskFactory.Startnewを)信じる

は、私にThreadPool.QueueUserWorkItemするために同様の働きスレッドプール内のスレッドに作業をキューイングします。

Pretty much

私が読んでいたことは、非同期/唯一の「時々」新しいスレッドを作成するようです。

実際、それは決してありません。マルチスレッドが必要な場合は、それを自分で実装する必要があります。新しいTask.RunメソッドはTask.Factory.StartNewの省略形です。スレッドプール上でタスクを開始する最も一般的な方法です。

IO完了ポートを扱っていたら、新しいスレッドを作成する必要がないことがわかりますが、それ以外の場合はそれが必要だと思います。

ビンゴ。したがって、Stream.ReadAsyncのようなメソッドは、StreamにIOCPがある場合、実際にはIOCPの周りにTaskラッパーを作成します。

I/O以外の非CPU「タスク」を作成することもできます。簡単な例はTask.Delayで、ある期間の後に完了するタスクを返します。

についてasync/awaitはあなたがスレッドプールにいくつかの作業をキューに入れることができるということですクールなもの(例えば、Task.Run)、例えば(いくつかのI/Oバウンドの操作を(例えば、Stream.ReadAsync)、行うと、他のいくつかの操作を行い、 Task.Delay)...彼らはすべてのタスクです!彼らは待つことができるか、Task.WhenAllのような組み合わせで使用することができます。

Taskを返すメソッドは、awaitにすることができます。asyncメソッドである必要はありません。したがってTask.DelayとI/Oバウンド操作では、タスクを作成して完了するのにTaskCompletionSourceを使用するだけです。スレッドプール上で実行されるのは、イベントが発生したときの実際のタスク完了(タイムアウト、I/O完了など)だけです。

私のFromCurrentSynchronizationContextについての理解は、いつも少しばかりであったと思います。私は本当に、本質的に、UIスレッドでした。

SynchronizationContextan articleを書きました。ほとんどの場合、現在のスレッドがUIスレッドの場合、UIコンテキストは、SynchronizationContext.Current

  • です。
  • は、現在のスレッドがASP.NET要求を処理している場合のASP.NET要求コンテキストです。
  • は、そうでない場合はスレッドプールコンテキストです。

どれスレッドSynchronizationContext、独自に設定することができますので、上記のルールに例外があります。

が nullでない場合、デフォルトTask awaiterが現在SynchronizationContextasync方法の残りをスケジュールすることに注意してください。それ以外の場合は現在のTaskSchedulerになります。今日はそれほど重要ではありませんが、近い将来は重要な違いになります。

私は自分自身のブログにasync/await introと書いています。スティーブントーブは最近、素晴らしいasync/await FAQを投稿しました。

「並行性」と「マルチスレッド化」については、this related SO questionを参照してください。私はasyncがマルチスレッド化されていてもいなくてもよい並行性を可能にすると言っています。 await Task.WhenAllまたはawait Task.WhenAnyを使用して同時処理を行うのは簡単で、スレッドプール(明示的にはTask.RunまたはConfigureAwait(false)など)を明示的に使用しない限り、同時に複数の同時処理を実行できます(複数のI/Oなど) Delayのようなタイプ) - スレッドに必要なスレッドはありません。 ASP.NETホストでは、実際には "ゼロ - スレッドの並行処理"が可能ですが、この種のシナリオでは「シングルスレッド同時実行」という用語を使用します。それはかなり甘いです。

+1

良い答え。また、http://www.infoq.com/articles/Async-API-Designとこの優れたプレゼンテーションをお勧めします:http://channel9.msdn.com/Events/TechEd/Europe/2013/DEV-B318。 – Philippe

+0

最初のリンクは無効です。 –

+0

@FelipeDeveza固定、ありがとう! –

8

非同期/基本的にContinueWith方法(Continuation Passing Styleで継続を)簡単になり待つ

それは、並行性を導入しない - あなたはまだしなければならないことを自分(またはフレームワークのメソッドの非同期バージョンを使用しています。)

だから、C#5バージョンは、次のようになります。

await Task.Run(() => DoSomeAsyncWork()); 
DoSomeWorkAfter(); 
+0

上記の例でDoSomeAsyncWork(async/waitバージョン)をどこで実行していますか? UIスレッドで実行されている場合、どのようにブロックされませんか? – coding4fun

+1

'DoSomeWorkAsync()'がvoidを返すか、待たずに何かを返すと、あなたの待ち時間のサンプルがコンパイルされません。あなたの最初の例から、私はあなたが別のスレッドで実行したいシーケンシャルメソッドであると仮定しました。並行処理を導入せずに 'Task'を返すように変更した場合は、それがブロックされます。つまり、UIスレッド上では通常のコードと同じように順番に実行されます。 'await'は、メソッドがまだ完了していない待ち時間を返す場合にのみ、yieldを返します。 –

+0

まあ、私はそれが実行するように選択するどこに実行されているとは思わないでしょう。 DoSomeAsyncWorkでコードを実行するためにTask.Runを使用しました。この場合、あなたの作業はスレッドプールスレッドで行われます。 –

関連する問題