Я працюю над розробкою програми, яка складається з трьох частин:
- єдиний потік, який спостерігає за певними подіями (створення файлів, зовнішні запити тощо)
- N робочих ниток, які реагують на ці події, обробляючи їх (кожен працівник обробляє та споживає одну подію, і обробка може зайняти різний час)
- контролер, який керує цими потоками та виконує обробку помилок (перезапуск потоків, реєстрація результатів)
Хоча це досить елементарно і не важко здійснити, мені цікаво, який би це був "правильний" спосіб зробити це (у цьому конкретному випадку на Java, але також високо оцінені відповіді на більш високі абстракції). На думку приходять дві стратегії:
Спостерігач / Спостереження: контрольну нитку спостерігає контролер. У випадку події контролер повідомляється і може призначити нове завдання вільному потоку з кешованого пулу ниток багаторазового використання (або чекати та кешувати завдання в черзі FIFO, якщо всі потоки зараз зайняті). Робочі потоки реалізують Callable і повертають успішно з результатом (або булевим значенням), або повертаються з помилкою, і в цьому випадку контролер може вирішити, що робити (залежно від характеру помилки, що трапилася).
Виробник / Споживач : Нитка перегляду поділяє BlockingQueue з контролером (чергою подій), а контролер ділиться двома з усіма працівниками (черга завдань та черга результатів). У випадку події поток спостереження ставить об’єкт завдання у чергу подій. Контролер приймає нові завдання з черги подій, переглядає їх і ставить їх у чергу завдань. Кожен працівник чекає нових завдань і приймає / споживає їх із черги завдань (першим приходить перший сервіс, керується самою чергою), переносячи результати або помилки назад у чергу-результат. Нарешті, контролер може отримати результати з черги результатів і вжити помилок у разі помилок.
Кінцеві результати обох підходів схожі, але кожен з них має невеликі відмінності:
За допомогою спостерігачів управління потоками є прямим, і кожне завдання приписується конкретному новонародженому працівнику. Накладні витрати для створення ниток можуть бути більшими, але не набагато завдяки кешованому пулу ниток. З іншого боку, шаблон спостерігача зводиться до одного спостерігача, а не до декількох, що не є саме тим, для чого він був розроблений.
Стратегію черги, здається, простіше розширити, наприклад, додавання декількох виробників замість одного є простим і не потребує змін. Мінус у тому, що всі потоки працюватимуть нескінченно, навіть коли взагалі не виконується жодна робота, а обробка помилок / результатів виглядає не так елегантно, як у першому рішенні.
Який був би найбільш підходящий підхід у цій ситуації і чому? Мені складно знайти відповіді на це запитання в Інтернеті, оскільки більшість прикладів стосуються лише явних випадків, таких як оновлення багатьох вікон на нове значення у випадку Observer або обробка кількома споживачами та виробниками. Будь-який вклад дуже вдячний.