Правильный способ управления потоками при обработке очереди

У меня есть очередь (на самом деле 3 разные очереди, но я не верю, что это будет иметь значение). Очередь содержит дискретные единицы работы.

У меня есть QueueManager, который порождает новый поток и возвращается. Новый поток просматривает очередь, получает часть работы, а затем порождает новый поток для ее обработки.

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

Для выполнения некоторых заданий потребуется некоторое время (минуты/часы), и я читал, что ThreadPool не является хорошим вариантом для всего, что занимает более нескольких секунд.

По сути, я хочу реализовать ThreadPool, но с немного большим контролем.

Итак... Каков стандартный и лучший способ добиться этого?

Кажется, мне нужно (при условии, что длина очереди> MaxThreadCount)

  • Создавайте новые задания, пока я не нажму MaxThreadCount
  • Дождитесь завершения любого потока
  • Получить следующее задание из очереди и назначить его потоку
  • Повторить
  • Если очередь пуста, подождите некоторое время, а затем перепроверьте

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

Многие задания связаны с повторным получением страниц с удаленных сайтов/API. Таким образом, я считаю, что мне было бы полезно иметь значительно больше потоков, чем ядер (поскольку большинство из них будут ждать сети). Таким образом, я не считаю WaitHandle подходящим, поскольку он имеет ограничение в 64 потока.

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

Если бы кто-то мог указать мне на хороший пример, это было бы превосходно.


person Basic    schedule 15.06.2012    source источник
comment
Согласен с ответом TPL. +1 Если узким местом является получение удаленных данных, можете ли вы их кэшировать? Вы повторно извлекаете одни и те же данные?   -  person paparazzo    schedule 15.06.2012
comment
@Blam На самом деле это интересная проблема - я могу кэшировать вещи и фактически делаю это (по крайней мере, все результаты хранятся в базе данных), но большая часть информации никогда не требуется дважды - и если это требуется во второй раз, чтобы увидеть, изменился ли источник, поэтому, хотя я записываю вывод, я, вероятно, никогда не буду обращаться к нему снова, кроме как для целей отчета.   -  person Basic    schedule 15.06.2012
comment
Вероятно, в вашем случае это не поможет, но один из вариантов - также сохранить данные в словаре, чтобы вы не так сильно нажимали на БД.   -  person paparazzo    schedule 15.06.2012
comment
@Blam Это все еще хорошая идея, спасибо - если производительность Db является проблемой, я потенциально мог бы использовать словарь в качестве кеша так же, как это делают процессоры, что, по крайней мере, минимизирует запросы Db.   -  person Basic    schedule 15.06.2012


Ответы (1)


Используйте TPL (библиотека параллельных задач). Он предназначен именно для этой цели.

http://msdn.microsoft.com/en-us/library/dd460717.aspx

В частности, вам следует создать TaskFactory с TaskScheduler, экземпляр которого MaximumConcurrencyLevel задано необходимое количество потоков. Затем вместо того, чтобы ставить рабочие элементы в очередь, вызовите TaskFactory.StartNew. Он будет обрабатывать очередь работы для вас.

person Chris Shain    schedule 15.06.2012
comment
Это поднимает второй вопрос, который я не считал применимым: я планировал использовать свой QueueManager для обновления базы данных по мере завершения каждого задания и сохранения результатов. Я использую EF, и многопоточность EF - это боль. Используя TPL, могу ли я по-прежнему иметь единую точку управления для поддержания состояния Db или мне придется иметь дело с этим из каждого потока? - person Basic; 15.06.2012
comment
В какой-то момент вам нужно будет иметь дело с несколькими потоками. У вас может быть однопоточный планировщик задач, но это как бы противоречит цели. - person Chris Shain; 15.06.2012