web-dev-qa-db-ja.com

TaskCreationOptions.LongRunningを使用する場合

私はこれをかなり長い間疑問に思っていましたが、実際には答えを見つけられませんでした。

タスクスケジューラがタスクを実行する場所のヒントであり、タスクスケジューラがそのタスクの非スレッドプールスレッドをインスタンス化することを決定できることを理解しています。

私が知らない(そして驚くべきことに、インターネット上のどこにも見つからない)のは、タスクを長時間実行するように指定するときの「経験則」です。 1秒ですか? 30秒?一分? 5分?アプリケーションが使用するタスクの量と関係がありますか?プログラマーとして、スレッドプール内の#threads、作成するタスクの数、同時に長時間実行するタスクの数で計算を行い、それに基づいて長時間実行タスクを使用するかどうかを判断する必要がありますか?

ここで何かを学びたいと思っています。

28
bas

定量化でき、既存のtpスレッドがすぐに完了しない場合、スレッドプールマネージャーはextraスレッドを最適値を超えて追加します。 SetMaxThreads()で設定された最大値まで、これを1秒間に2回行います。 very高いデフォルト値があります。最適なのは、マシンで使用可能なプロセッサコアの数です。4が一般的です。使用可能なコアよりも多くのスレッドを実行すると、コンテキスト切り替えのオーバーヘッドのために有害になる可能性があります。

これは、これらの既存のスレッドが十分なコードを実行していないため、これらの既存のスレッドが進行しないという仮定に基づいてこれを行います。つまり、I/Oまたはロックをブロックしすぎます。そのため、このようなスレッドはコアを十分に効率的に使用せず、余分なスレッドを実行できるようにすることは、プロセッサの使用率を高め、より多くの作業を完了するために完全に適切です。

そのため、スレッドが0.5秒以上かかると「長時間実行」されます。これは非常に長い時間であり、最新のデスクトップクラスのマシンで約40億のプロセッサ命令に相当することに注意してください。 piの値を数十億桁まで計算するなどの計算量の多いコードを実行して、実際にこれらの40億命令を実行しない限り、実際のスレッドはdoesがあまりにも頻繁にブロックされる場合にのみこの時間がかかります。これは非常に一般的です。dbaseクエリのようなものは、多くの場合、低速であり、ワーカースレッドで実行され、CPUをほとんど消費しません。

それ以外の場合は、スレッドプールマネージャが行う仮定が正確であることを確認するのはユーザー次第です。タスクはプロセッサを効率的に使用していないため、時間がかかるはずです。タスクマネージャーは、プログラムでプロセッサコアが何を実行しているかを簡単に確認する方法です。ただし、実行しているコードを正確に通知するわけではありません。スレッドが単独で実行されていることを確認するには、単体テストが必要です。 LongRunningを使用するのが適切な選択であったことを伝える究極の、そして完全に正確な方法は、アプリが実際により多くの作業を完了したことを確認することです。

25
Hans Passant

ハンスの答えを修正しますが、私はほとんど同意します。

LongRunningを指定する最も重要な理由は、実際に保証されたほぼ即時の実行を取得することです。スレッドプールがワークアイテムにスレッドを発行するのを待つことはありません。 OSはスレッドをスケジュールしないように自由に設定できるので、「実際に」と言っています。ただし、CPUの一部を取得します。通常は、発生するまで非常に長い時間はかかりません。

LongRunningを指定して、キューの前にジャンプしています。負荷がかかっている場合、スレッドプールが1秒あたり2つのスレッドを発行するのを待つ必要はありません。

したがって、必ずしも最も効率的な方法ではなく、タイムリーで安定した方法で発生する必要があることにLongRunningを使用します。たとえば、UIの一部の動作、ゲームループ、進捗レポートなど...

スレッドの開始と停止には、1ms程度のCPU時間がかかります。これは、スレッドプールの作業項目を発行することをはるかに超えています。これをベンチマークして、1秒あたり3Mのアイテムが発行および完了するようにしました。ベンチマークは非常に人工的なものでしたが、規模は適切です。

LongRunningはヒントとして文書化されていますが、実際には絶対に効果的です。ヒントを考慮するヒューリスティックはありません。それは正しいと仮定されています。

14
usr

タスクを長期実行として指定するタイミング

それはどのタスクをしているのかによります。タスクにwhile(true) {...}が含まれ、アプリケーションがシャットダウンするまで存続する場合は、LongRunningを指定するのが理にかなっています。タスクをqueueに作成し、現在のスレッドをブロックしないようにする場合は、実際には気にしません(何も指定しないでください)。

他のタスクが何をしているかに依存します。 LongRunningの有無にかかわらず、いくつかのタスクを実行するかどうかは関係ありません。しかし、それぞれが新しいスレッドを要求する何千ものタスクを作成するのは問題かもしれません。または逆に、指定しないで thread starvation が発生する場合があります。

これについて考える簡単な方法の1つは、新しいスレッドで実行する新しいタスクを好むか、気にしないかです。最初の場合-LongRunningOptionを使用します。これは、 意味しない 別のスレッドで実行されるタスクです。指定する必要がある場合に適した基準です。

例えば。 ContinueWithを使用する場合、LongRunningExecuteSynchronouslyと反対です(両方が指定されないようにするために check があります)。複数の継続がある場合は、キューのオーバーヘッドを回避し、同じスレッドまたは反対で特定の継続を実行することができます-継続のいずれかが他の干渉しないようにしたい場合は、LongRunningを具体的に使用できます。 ExecuteSynchronouslyの詳細については、 この記事 (および this )を参照してください。

3
Sinatr

長時間実行されるタスクとは、待機状態になり、実行中のスレッドをブロックする可能性のあるタスク、またはCPU時間を過度に消費するタスクです(このタスクに戻ります)。

この定義は広すぎる、多くのタスクが長時間実行されると言う人もいるかもしれませんが、待機が小さなタイムアウトに制限されていても、タスクはまだCPUを効果的に使用していないと考えてください。これらのタスクの量が増えると、MinWorkerThreadsを超えて線形にスケーリングしないことがわかります( _ThreadPool.SetMinThreads_ を参照)、劣化は本当に悪いです。

すべてのI/O(ファイル、ネットワーク、DBなど)を非同期に切り替えるアプローチ。

また、CPUを大量に消費する計算のために、長時間実行されるタスクもあります。

アプローチは、特定のポイントにawait Task.Yield()を挿入するなどの計算を延期するか、できれば1つのタスクを次々にスケジュールして明示的に計算を延期します。制限時間。

「時間がかかりすぎる」のはあなた次第です。

スレッドプールを共有している環境では、いつでも時間がかかりすぎるため、適切な値を選択する必要があります。

例えば。 IISのASP.NETで、最も一般的なリクエストのリクエストごとにかかる平均時間を確認してください。同様に、スレッドプールが使用されるサービスでは、メッセージキューを処理するには、メッセージごとの平均を使用します。

より一般的には、「時間が長すぎる」とは、作業項目が処理されるよりも早くキューに入れられることです。作業が急増する可能性があるため、1秒、1分、10分など、重要な時間単位でこれを平均する必要があります。SLAがある場合は、この間隔をどこかで定義する必要があります。

賢明な値を取得した後、実際には、値を増やしてもよいか、値を下げなければならないかを確認する必要があります。多くの場合、大幅なパフォーマンスの違いが見られる場合を除いて、not増やすことをお勧めします。 「有意」とは、処理されたアイテムの数が線形よりも増加することを意味します。したがって、線形(または線形より下の場合は発生する可能性があります)にしないでください。


私の経験では、これらの定義のいずれかによって長時間実行されるタスクがある場合、通常、独自のスレッドまたはスレッドのセットを管理する方が良いでしょう。

2
acelent