Я работаю над дизайном приложения, которое состоит из трех частей:
- один поток, который отслеживает определенные события (создание файлов, внешние запросы и т. д.)
- N рабочих потоков, которые отвечают на эти события, обрабатывая их (каждый рабочий обрабатывает и использует одно событие, и обработка может занять переменное время)
- контроллер, который управляет этими потоками и обрабатывает ошибки (перезапуск потоков, регистрация результатов)
Хотя это довольно просто и не сложно реализовать, мне интересно, что было бы «правильным» способом сделать это (в данном конкретном случае в Java, но также приветствуются ответы с более высокой абстракцией). На ум приходят две стратегии:
Наблюдатель / Наблюдаемый: наблюдающий поток контролируется контроллером. В случае возникновения события контроллер уведомляется и может назначить новую задачу свободному потоку из пула многократно кэшированных потоков (или подождать и кэшировать задачи в очереди FIFO, если все потоки в данный момент заняты). Рабочие потоки реализуют Callable и либо возвращают успешно результат (или логическое значение), либо возвращают с ошибкой, и в этом случае контроллер может решить, что делать (в зависимости от характера возникшей ошибки).
Производитель / Потребитель : Наблюдающий поток совместно использует BlockingQueue с контроллером (очередь событий), а контроллер делит два со всеми работниками (очередь задач и очередь результатов). В случае события наблюдающий поток помещает объект задачи в очередь событий. Контроллер берет новые задачи из очереди событий, просматривает их и помещает в очередь задач. Каждый работник ждет новых задач и получает / использует их из очереди задач (первым пришел, первым обслужен, управляется самой очередью), помещая результаты или ошибки обратно в очередь результатов. Наконец, контроллер может получить результаты из очереди результатов и предпринять соответствующие шаги в случае ошибок.
Конечные результаты обоих подходов похожи, но каждый из них имеет небольшие различия:
В Observers управление потоками осуществляется напрямую, и каждая задача присваивается определенному новому порожденному работнику. Накладные расходы на создание потоков могут быть выше, но не очень благодаря кэшированному пулу потоков. С другой стороны, шаблон Observer сводится к одному Observer вместо нескольких, что не совсем то, для чего он был разработан.
Стратегию очереди, кажется, легче расширить, например, добавление нескольких производителей вместо одного является простым и не требует каких-либо изменений. Недостатком является то, что все потоки будут работать бесконечно, даже если они вообще не будут работать, а обработка ошибок / результатов не будет выглядеть так элегантно, как в первом решении.
Какой подход был бы наиболее подходящим в этой ситуации и почему? Мне было трудно найти ответы на этот вопрос в Интернете, потому что большинство примеров имеют дело только с понятными случаями, такими как обновление многих окон новым значением в случае Observer или обработка с несколькими потребителями и производителями. Любой вклад с благодарностью.