Dove implementare la logica di sicurezza del thread per la mia struttura dati della coda?

2

Confesserò immediatamente che faccio molto piccolo multithreading, quindi le domande sullo stile e l'organizzazione in questo dominio mi sembrano un po 'nuove.

Ho scritto una struttura dati (in Python, se questo è importante) che implementa un tipo speciale di coda. Mi piacerebbe usare questa coda per passare i messaggi tra i thread. Attualmente, l'unica logica che ho non ha nulla a che fare con i thread e semplicemente implementa la "salsa speciale" di organizzare i messaggi in questa coda.

Mi piace avere questa coda come una struttura dati che non ha nulla a che fare con i thread, proprio come qualsiasi altra struttura di dati. Tuttavia, ho bisogno di usare mutex, variabili di condizione, ecc. Per implementare la logica di sicurezza del thread. La domanda è: dove va questa logica?

Scrivo una classe, diciamo, SpecialQueueConcurrent derivata da SpecialQueue ? Scrivo una classe che avvolge SpecialQueue invece di derivarne? Fornisco un'opzione per renderlo concorrente nel costruttore?

Come si fa normalmente?

    
posta Phonon 23.05.2014 - 06:33
fonte

1 risposta

3

Dipende dai casi d'uso della struttura dati e da qualsiasi restrizione di quell'uso che devi rispettare (a proposito di quest'ultimo).

I casi d'uso

Esistono alcuni casi d'uso che potrebbero determinare l'approccio corretto nella scelta o nell'implementazione di una struttura di dati simultanea. È necessario considerare:

  • L'implementazione della concorrenza supportata dalla struttura:

    • Il blocco rigoroso potrebbe garantire la massima coerenza dei dati al prezzo dei blocchi e creare colli di bottiglia nelle prestazioni.
    • Le raccolte concorrenti senza blocco sono implementazioni speciali che funzionano senza blocchi , ma hanno (nella maggior parte dei casi accettabile) la possibilità che un thread non legga le modifiche dell'altro. Questi non sono gli stessi del caso dell'utilizzo di una raccolta standard senza blocchi, in quanto la collezione lock-free non consente di danneggiare la raccolta stessa. Con le collezioni lock-free, lo svantaggio è che più thread potrebbero eseguire la stessa operazione, mentre solo uno di essi verrebbe effettivamente preso in considerazione. A seconda del contesto, questo è spesso giustificato e accettabile, poiché queste raccolte stanno introducendo le migliori prestazioni nell'elaborazione simultanea, rispetto all'utilizzo del blocco, tali situazioni di elaborazione ripetitive sono relativamente rare e di solito non portano alla corruzione di tutti i dati / stato.
    • I bucket indipendenti con i propri blocchi sono un altro approccio che tenta di consentire l'uso simultaneo della raccolta. In questo approccio, la raccolta viene suddivisa internamente in più contenitori con i propri blocchi, che possono essere utilizzati da più thread. Un esempio è un dizionario concorrente che userebbe internamente una serie di dizionari. In base al codice hash della chiave, il dizionario deciderebbe quale dizionario secondario utilizzare dal thread corrispondente e limiterà il blocco solo a quel dizionario figlio. Un altro thread che verrebbe indirizzato a un altro dizionario secondario non verrà bloccato. In alternativa, una serie di oggetti di blocco può essere utilizzata lungo un singolo contenitore con lo stesso effetto. Il problema con questo approccio è che la raccolta stessa decide su quale bucket il thread attuale lavora (e blocca), quindi non si ha il controllo sul fatto che certe operazioni saranno effettivamente concomitanti (se si manipolano da più bucket) o meno (se appaiono per influenzare lo stesso secchio).
  • Il numero di thread che trattano simultaneamente una singola istanza della struttura. Ciò determinerà la possibilità di potenziali usi simultanei che potrebbero causare il blocco.

  • Il rapporto tra operazioni di lettura e scrittura. Questo potrebbe aiutarti a decidere quale comportamento concorrente tra quelli sopra elencati è adatto. Ad esempio una collezione che viene più spesso letta da, che aggiornata, sarebbe più performante se ha un'implementazione senza blocco o se viene utilizzato un blocco di lettura-scrittura. Il blocco di lettura / scrittura è un blocco speciale che consente di controllare se deve bloccare le letture quando viene eseguita una scrittura, ma per consentire letture simultanee.
  • L'importanza della coerenza dei dati all'interno della struttura. Ciò è influenzato dai diversi approcci all'implementazione di tale struttura, come discusso sopra.

Le restrizioni

Per limitazioni mi riferisco al codice che stai usando. Se interagisci con un'API di terze parti o sei limitato dal tipo di raccolta / struttura che devi utilizzare (se devi passarlo al metodo dell'API), o sei obbligato a implementare determinate interfacce, devi scoprire i potenziali usi di questo codice e un'implementazione adatta che si adatterà. La maggior parte delle attuali implementazioni simultanee di raccolta sarebbe comunque compatibile almeno con le interfacce iterable e di raccolta.

Note di implementazione

Se vuoi basare la tua struttura su una collezione esistente, ti consiglio di completarla, piuttosto che usare l'ereditarietà. Wrapping ti garantirà che non perderai un metodo che deve essere sincronizzato e che ti darà la libertà di scegliere il tuo comportamento di blocco. Puoi sempre implementare un'interfaccia di raccolta standard per rendere la tua raccolta utilizzabile da altre API (vedi la sezione restrizioni sopra)

    
risposta data 23.05.2014 - 09:54
fonte

Leggi altre domande sui tag