Pracuję nad projektem aplikacji, która składa się z trzech części:
- pojedynczy wątek, który obserwuje określone zdarzenia (tworzenie plików, żądania zewnętrzne itp.)
- N wątków roboczych, które reagują na te zdarzenia, przetwarzając je (każdy proces roboczy przetwarza i zużywa pojedyncze zdarzenie, a przetwarzanie może zająć zmienny czas)
- kontroler, który zarządza tymi wątkami i obsługuje obsługę błędów (restartowanie wątków, rejestrowanie wyników)
Chociaż jest to dość podstawowe i nietrudne do wdrożenia, zastanawiam się, jaki byłby to „właściwy” sposób (w tym konkretnym przypadku w Javie, ale docenia się również odpowiedzi na abstrakcję). Przychodzą mi na myśl dwie strategie:
Obserwator / Obserwowalny: Obserwujący wątek jest obserwowany przez kontroler. W przypadku zdarzenia kontroler jest następnie powiadamiany i może przypisać nowe zadanie do wolnego wątku z puli buforowanych wątków wielokrotnego użytku (lub czekać i buforować zadania w kolejce FIFO, jeśli wszystkie wątki są aktualnie zajęte). Wątki robocze implementują możliwość wywoływania i albo zwracają wynik z wynikiem (lub wartością logiczną), albo zwracają z błędem, w którym to przypadku kontroler może zdecydować, co zrobić (w zależności od rodzaju popełnionego błędu).
Producent / konsument : Wątek obserwujący współdzieli BlockingQueue z kontrolerem (kolejka zdarzeń), a kontroler dzieli dwa ze wszystkimi pracownikami (kolejka zadań i kolejka wyników). W przypadku zdarzenia wątek obserwujący umieszcza obiekt zadania w kolejce zdarzeń. Kontroler pobiera nowe zadania z kolejki zdarzeń, przegląda je i umieszcza w kolejce zadań. Każdy pracownik czeka na nowe zadania i pobiera je z kolejki zadań (kto pierwszy ten lepszy, zarządzany przez samą kolejkę), odkładając wyniki lub błędy z powrotem do kolejki wyników. Na koniec kontroler może pobrać wyniki z kolejki wyników i podjąć odpowiednie kroki w przypadku błędów.
Wyniki końcowe obu podejść są podobne, ale każde z nich ma niewielkie różnice:
W przypadku obserwatorów kontrola wątków jest bezpośrednia, a każde zadanie przypisywane jest do konkretnego nowego spawnowanego pracownika. Narzut związany z tworzeniem wątków może być wyższy, ale niewiele dzięki buforowanej puli wątków. Z drugiej strony wzorzec Obserwatora jest zredukowany do jednego Obserwatora zamiast wielu, co nie jest dokładnie tym, do czego został zaprojektowany.
Wydaje się, że strategia kolejek jest łatwiejsza do rozszerzenia, na przykład dodanie wielu producentów zamiast jednego jest proste i nie wymaga żadnych zmian. Minusem jest to, że wszystkie wątki działałyby w nieskończoność, nawet gdy w ogóle nie wykonywały żadnej pracy, a obsługa błędów / wyników nie wygląda tak elegancko jak w pierwszym rozwiązaniu.
Jakie byłoby najbardziej odpowiednie podejście w tej sytuacji i dlaczego? Trudno mi znaleźć odpowiedzi na to pytanie w Internecie, ponieważ większość przykładów dotyczy tylko jasnych przypadków, takich jak aktualizacja wielu okien o nową wartość w przypadku Observer lub przetwarzanie z wieloma konsumentami i producentami. Wszelkie uwagi są mile widziane.