Differenza tra consumatore / produttore e osservatore / osservabile

15

Sto lavorando alla progettazione di un'applicazione composta da tre parti:

  • un singolo thread che osserva il verificarsi di determinati eventi (creazione di file, richieste esterne ecc.)
  • N thread di lavoro che rispondono a questi eventi elaborandoli (ciascun worker elabora e consuma un singolo evento e l'elaborazione può richiedere tempo variabile)
  • un controller che gestisce questi thread e gestisce gli errori (riavvio dei thread, registrazione dei risultati)

Anche se questo è abbastanza semplice e non è difficile da implementare, mi chiedo quale sarebbe il modo "giusto" per farlo (in questo caso concreto in Java, ma sono anche apprezzate le risposte più astratte). Vengono in mente due strategie:

  • Observer / Observable: il thread di osservazione viene osservato dal controller. In caso di un evento, il controllore viene quindi informato e può assegnare la nuova attività a un thread libero da un pool di thread memorizzato nella cache (oppure attendere e memorizzare nella cache le attività nella coda FIFO se tutti i thread sono attualmente occupati). I thread worker implementano Callable e restituiscono correttamente con il risultato (o un valore booleano) oppure restituiscono con un errore, nel qual caso il controller può decidere cosa fare (in base alla natura dell'errore che si è verificato).

  • Producer / Consumer : il thread di visualizzazione condivide un BlockingQueue con il controller (coda evento) e il controller ne condivide due con tutti i worker (coda delle attività e coda dei risultati). In caso di un evento, il thread di sorveglianza inserisce un oggetto task nella coda degli eventi. Il controllore acquisisce nuove attività dalla coda degli eventi, le rivede e le inserisce nella coda delle attività. Ogni lavoratore attende nuove attività e le prende / consuma dalla coda delle attività (primo arrivato, primo servito, gestito dalla coda stessa), riportando i risultati o gli errori nella coda dei risultati. Infine, il controller può recuperare i risultati dalla coda dei risultati e prendere le misure necessarie in caso di errori.

I risultati finali di entrambi gli approcci sono simili, ma ognuno presenta lievi differenze:

Con gli osservatori, il controllo dei thread è diretto e ogni attività è attribuita a un nuovo lavoratore spawn. L'overhead per la creazione di thread potrebbe essere più elevato, ma non molto grazie al pool di thread memorizzato nella cache. D'altra parte, il pattern Observer è ridotto a un singolo Observer invece che a più, il che non è esattamente quello per cui è stato progettato.

La strategia della coda sembra essere più facile da estendere, ad esempio l'aggiunta di più produttori invece di uno è semplice e non richiede alcuna modifica. Il rovescio della medaglia è che tutti i thread sarebbero eseguiti indefinitamente, anche quando non funzionano affatto, e la gestione degli errori / dei risultati non sembra elegante come nella prima soluzione.

Quale sarebbe l'approccio più appropriato in questa situazione e perché? Ho trovato difficile trovare risposte a questa domanda online, perché la maggior parte degli esempi tratta solo casi chiari, come l'aggiornamento di molte finestre con un nuovo valore nel caso Observer o l'elaborazione con più consumatori e produttori. Qualsiasi input è molto apprezzato.

    
posta user183536 14.06.2015 - 17:00
fonte

1 risposta

10

Sei abbastanza vicino a rispondere alla tua stessa domanda. :)

Nel pattern Observable / Observer (nota il flip), ci sono tre cose da tenere a mente:

  1. Generalmente, la notifica della modifica, ad esempio "payload", è nell'osservatore.
  2. L'osservabile esiste .
  3. Gli osservatori devono essere noti all'osservatore esistente (oppure non hanno nulla da osservare in poi.

Combinando questi punti, ciò che è implicito è che l'osservabile sa quali sono i suoi componenti a valle, cioè gli osservatori. Il flusso di dati è intrinsecamente guidato dall'osservabile: gli osservatori semplicemente "vivono e muoiono" in base a ciò su cui stanno osservando.

Nel pattern Producer / Consumer, ottieni un'interazione molto diversa:

  1. Generalmente, il payload esiste indipendentemente dal produttore responsabile della sua produzione.
  2. I produttori non sanno come o quando i consumatori sono attivi.
  3. I consumatori non devono necessariamente conoscere il produttore del carico utile.

Il flusso di dati è ora completamente separato tra un produttore e un consumatore - tutto il produttore sa che ha un output e tutto il consumatore sa che ha un input. È importante sottolineare che ciò significa che produttori e consumatori possono esistere interamente senza la presenza dell'altro.

Un'altra differenza non così sottile è che più osservatori sullo stesso osservabili di solito ottengono lo stesso carico utile (a meno che non ci sia un'implementazione non convenzionale), mentre più consumatori fuori dallo stesso em> produttore non può. Questo dipende se l'intermediario è un approccio simile a una coda o un argomento. Il primo trasmette un messaggio diverso per ciascun consumatore, mentre il secondo garantisce (o tenta di) che tutti i consumatori elaborino i messaggi per messaggio.

Per adattarli alla tua applicazione:

  • Nel pattern Observable / Observer, ogni volta che il thread di visualizzazione sta inizializzando, deve sapere come informare il controller. Come osservatore, è probabile che il controller attenda una notifica dal thread di sorveglianza prima che lasci che i thread gestiscano la modifica.
  • Nel pattern Producer / Consumer, il tuo thread di osservazione deve solo conoscere la presenza della coda degli eventi e interagire esclusivamente con quello. Come consumatore, il controller esegue il polling della coda degli eventi e, una volta ottenuto un nuovo payload, lascia che i thread lo gestiscano.

Pertanto, per rispondere direttamente alla tua domanda: se vuoi mantenere un certo livello di separazione tra il tuo thread di osservazione e il tuo controller in modo da poterli operare in modo indipendente, dovresti orientarti verso il pattern Producer / Consumer.

    
risposta data 16.06.2015 - 11:43
fonte