私は並列プログラミングに不慣れです。.NETで使用できるクラスは2つあります:Task
とThread
。
だから、私の質問は次のとおりです。
- それらのクラスの違いは何ですか?
Thread
いつ使用するのが良いTask
ですか(またはその逆)?
私は並列プログラミングに不慣れです。.NETで使用できるクラスは2つあります:Task
とThread
。
だから、私の質問は次のとおりです。
Thread
いつ使用するのが良いTask
ですか(またはその逆)?Thread
は低レベルの概念です。スレッドを直接開始する場合は、スレッドプールなどで実行するのではなく、別のスレッドになることがわかります。
Task
ただし、これは「コードを実行する場所」の単なる抽象化ではありません。実際には、「将来の結果の約束」にすぎません。いくつかの異なる例として:
Task.Delay
実際のCPU時間は必要ありません。タイマーを設定して将来オフにするようなものですWebClient.DownloadStringTaskAsync
ローカルで多くのCPU時間を必要としません。これは、ほとんどの時間をネットワーク遅延またはリモート作業(Webサーバーで)に費やす可能性が高い結果を表しています。Task.Run()
によって返されるタスクは、「このコードを個別に実行してほしい」というものです。そのコードが実行される正確なスレッドは、いくつかの要因によって異なります。Task<T>
抽象化は、C#5での非同期サポートにとって極めて重要であることに注意してください。
一般に、可能な限り高レベルの抽象化を使用することをお勧めします。最新のC#コードでは、独自のスレッドを明示的に開始する必要はほとんどありません。
通常、タスクはスレッドよりも高レベルの概念であると聞きます...そしてそれがこのフレーズの意味です:
Abort / ThreadAbortedExceptionを使用することはできません。token.IsCancellationRequested
フラグを定期的にテストする「ビジネスコード」でキャンセルイベントをサポートする必要があります(dbへの長い接続やタイムアウトのない接続も避けてください。そうしないと、このフラグをテストする機会がありません)。同様の理由Thread.Sleep(delay)
で、呼び出しを呼び出しに置き換える必要があり Task.Delay(delay, token)
ます(遅延を中断する可能性があるためにトークンを内部に渡す)。
タスクにはスレッドSuspend
とResume
メソッドの機能はありません。タスクのインスタンスも再利用できません。
しかし、2つの新しいツールがあります。
a)継続
// continuation with ContinueWhenAll - execute the delegate, when ALL
// tasks[] had been finished; other option is ContinueWhenAny
Task.Factory.ContinueWhenAll(
tasks,
() => {
int answer = tasks[0].Result + tasks[1].Result;
Console.WriteLine("The answer is {0}", answer);
}
);
b)ネストされた/子タスク
//StartNew - starts task immediately, parent ends whith child
var parent = Task.Factory.StartNew
(() => {
var child = Task.Factory.StartNew(() =>
{
//...
});
},
TaskCreationOptions.AttachedToParent
);
したがって、システムスレッドはタスクから完全に隠されていますが、それでもタスクのコードは具体的なシステムスレッドで実行されます。システムスレッドはタスクのリソースであり、もちろん、タスクの並列実行の内部にはスレッドプールがあります。スレッドが新しいタスクを実行する方法には、さまざまな戦略があります。別の共有リソースTaskSchedulerがそれを気にします。TaskSchedulerが解決するいくつかの問題1)同じスレッドでタスクとその継続を実行することを好み、スイッチングコストを最小限に抑えます-別名インライン実行)2)開始された順序でタスクを実行することを好みます-別名PreferFairness 3)非アクティブなスレッド間でタスクをより効果的に分散します「タスクアクティビティの事前知識」に応じて-別名仕事を盗む。重要:一般に、「非同期」は「並列」と同じではありません。TaskSchedulerオプションを使用すると、非同期タスクを1つのスレッドで同期的に実行するように設定できます。並列コード実行を表現するには、(タスクよりも)高度な抽象化を使用できます:Parallel.ForEach
、、。PLINQ
Dataflow
タスクはC#async / await機能(別名Promise Model )と統合されています。たとえばrequestButton.Clicked += async (o, e) => ProcessResponce(await client.RequestAsync(e.ResourceName));
、実行するclient.RequestAsync
とUIスレッドがブロックされません。重要:内部では、Clicked
デリゲート呼び出しは完全に定期的です(すべてのスレッド化はコンパイラーによって行われます)。
それは選択をするのに十分です。ハングする傾向のあるレガシーAPIの呼び出しのキャンセル機能(タイムアウトレス接続など)をサポートする必要があり、この場合はThread.Abort()をサポートする場合、またはマルチスレッドバックグラウンド計算を作成していて、Suspend/Resumeを使用してスレッド間の切り替えを最適化する場合、つまり、並列実行を手動で管理することを意味します-スレッドにとどまります。それ以外の場合は、タスクに移動します。タスクのグループを簡単に操作でき、言語に統合され、開発者の生産性が向上します-タスク並列ライブラリ(TPL)。
このThread
クラスは、Windowsでスレッドを作成および操作するために使用されます。
ATask
は非同期操作を表し、タスクを非同期および並列で実行するためのAPIのセットであるタスク並列ライブラリの一部です。
昔(つまりTPL以前)は、Thread
クラスを使用することがバックグラウンドまたは並列でコードを実行する標準的な方法の1つでしたが(より良い代替手段は多くの場合、を使用することでしたThreadPool
)、これは面倒であり、いくつかの欠点があります。特に、バックグラウンドでタスクを実行するためのまったく新しいスレッドを作成することによるパフォーマンスのオーバーヘッドがあります。
現在、タスクとTPLを使用することは、システムリソースのはるかに効率的な使用を可能にする抽象化を提供するため、90%の確率ではるかに優れたソリューションです。コードを実行しているスレッドを明示的に制御したいシナリオがいくつかあると思いますが、一般的に、非同期で何かを実行したい場合は、最初の呼び出しポートをTPLにする必要があります。