У нас была небольшая проблема. :)
Мы хотим обеспечить, чтобы только N потоков выполняли фоновые задачи в любое время. Для этого мы использовали фиксированный исполнитель пула потоков. Казалось, работает нормально.
Тогда мы нашли проблему. Предположим, у вас есть класс, который использует executor для выполнения некоторой параллельной работы, а затем вызывает другой класс, находясь в потоке executor, который также выполняет некоторую параллельную работу, намереваясь ждать его. Вот что происходит:
- Основной поток вызывает метод первого уровня.
- Этот метод думает, что он может распараллелить на 16 задач и разбивает свою работу.
- 16 заданий передаются исполнителю.
- Главный поток начинает ждать завершения своих задач.
- Предположим, что доступно четыре потока, и первые четыре задачи выполняются. Таким образом, в очереди осталось 12 задач.
- Теперь одна из этих задач вызывает другой метод.
- Этот новый метод считает, что он может распараллеливаться на 2 задачи. Скажем, это первый шаг в параллельной сортировке слиянием или что-то в этом роде.
- 2 задания передаются исполнителю.
- Этот поток теперь начинает ждать завершения своих задач.
Ой-ой. Таким образом, на этом этапе все четыре потока теперь будут ожидать завершения задач, но они совместно блокируют исполнителя, фактически выполняющего эти задачи.
Решение 1 этой проблемы заключалось в следующем: при отправке нового задания исполнителю, если мы уже запускаем все наши потоки, и мы уже работаем в одном из потоков исполнителя, запустите задачу встроенным образом. Это работало нормально в течение 10 месяцев, но теперь мы столкнулись с проблемой. Если новые задачи, которые он отправляет, все еще относительно велики, то вы можете попасть в ситуацию, когда новая задача блокирует метод от добавления других задач в очередь, которые в противном случае могли бы быть подхвачены другими рабочими потоками. Таким образом, вы получаете периоды огромных задержек, пока поток обрабатывает работу в потоке.
Есть ли лучшее решение основной проблемы - выполнение потенциально неограниченного дерева фоновых задач? Я понимаю, что .NET-эквивалент службы executor обладает некой встроенной способностью красть из очереди, что предотвращает возникновение исходной тупиковой ситуации, что, насколько я могу судить, является идеальным решением. Но как насчет того, чтобы на земле Явы?