Лучший способ ограничить количество активных задач, выполняемых через библиотеку параллельных задач

Рассмотрим очередь сlot рабочих мест, которые нуждаются в обработке. Ограничение очереди - это возможность получить только 1 работу за раз, и нет возможности узнать, сколько там рабочих мест. Задания выполняются за 10 секунд и требуют много времени для ожидания ответов от веб-служб, поэтому они не связаны с процессором.

Если я использую что-то вроде этого

while (true)
{
   var job = Queue.PopJob();
   if (job == null)
      break;
   Task.Factory.StartNew(job.Execute); 
}

Тогда он будет яростно извлекать задания из очереди гораздо быстрее, чем сможет их завершить, исчерпать память и упасть на задницу. & GT;. & Л;

Я не могу использовать (я не думаю)ParallelOptions.MaxDegreeOfParallelism потому что я не могу использовать Parallel.Invoke или Parallel.ForEach

3 варианта, которые я нашел

Replace Task.Factory.StartNew with

Task task = new Task(job.Execute,TaskCreationOptions.LongRunning)
task.Start();

Which seems to somewhat solve the problem but I am not clear exactly what this is doing and if this is the best method.

Create a custom task scheduler that limits the degree of concurrency

Use something like BlockingCollection to add jobs to collection when started and remove when finished to limit number that can be running.

С # 1 я должен верить, что правильное решение принимается автоматически, # 2 / # 3 я должен решить максимальное количество задач, которые могут выполняться самостоятельно.

Правильно ли я понял это - какой путь лучше или есть другой путь?

EDIT - Это то, что я придумал из приведенных ниже ответов, модель производитель-потребитель.

Кроме того, общая цель пропускной способности состояла не в том, чтобы снять очереди с заданий быстрее, чем можно было бы обработать, и не иметь очереди опроса для нескольких потоков (здесь не показано, но это неблокирующая операция и приведет к огромным транзакционным издержкам при опросе с высокой частотой из нескольких мест) ,

// 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;
    }
}

Ответы на вопрос(6)

Ваш ответ на вопрос