処理が必要な多くのジョブを保持しているキューについて考えてみます。キューの制限は、一度に1つのジョブしか取得できず、ジョブの数を知る方法がありません。ジョブは完了するのに10秒かかり、Webサービスからの応答を多くの待機が必要になるため、CPUに依存しません。
このようなものを使うと
while (true)
{
var job = Queue.PopJob();
if (job == null)
break;
Task.Factory.StartNew(job.Execute);
}
次に、ジョブを完了するよりもはるかに速くキューから猛烈にポップし、メモリを使い果たして、お尻に落ちます。>。<
Parallel.InvokeまたはParallel.ForEachを使用できないため、ParallelOptions.MaxDegreeOfParallelismを使用できません(私は思いません)
私が見つけた3つの選択肢
Task.Factory.StartNewをに置き換えます
Task task = new Task(job.Execute,TaskCreationOptions.LongRunning) task.Start();
これは問題をいくらか解決するようですが、これが何をしているのか、そしてこれが最良の方法であるかどうかは正確にはわかりません。
BlockingCollectionのようなものを使用して、開始時にコレクションにジョブを追加し、終了時に削除して、実行できる数を制限します。
#1では、正しい決定が自動的に行われることを信頼する必要があります。#2 /#3自分で実行できるタスクの最大数を計算する必要があります。
私はこれを正しく理解しましたか?どちらがより良い方法ですか、それとも別の方法がありますか?
編集-これは、以下の回答、生産者/消費者パターンから私が思いついたものです。
全体的なスループットの目的は、処理可能な速度よりも速くジョブをデキューすることではなく、複数のスレッドのポーリングキューを持たないことでした(ここには示されていませんが、これは非ブロッキングオペレーションであり、複数の場所から高頻度でポーリングされると、膨大なトランザクションコストが発生します) 。
// BlockingCollection<>(1) will block if try to add more than 1 job to queue (no
// point in being greedy!), or is empty on take.
var BlockingCollection<Job> jobs = new BlockingCollection<Job>(1);
// Setup a number of consumer threads.
// Determine MAX_CONSUMER_THREADS empirically, if 4 core CPU and 50% of time
// in job is blocked waiting IO then likely be 8.
for(int numConsumers = 0; numConsumers < MAX_CONSUMER_THREADS; numConsumers++)
{
Thread consumer = new Thread(() =>
{
while (!jobs.IsCompleted)
{
var job = jobs.Take();
job.Execute();
}
}
consumer.Start();
}
// Producer to take items of queue and put in blocking collection ready for processing
while (true)
{
var job = Queue.PopJob();
if (job != null)
jobs.Add(job);
else
{
jobs.CompletedAdding()
// May need to wait for running jobs to finish
break;
}
}