2012-08-04 14 views
6

Ho il compito di modificare un programma C sincrono in modo che possa essere eseguito in parallelo. L'obiettivo è di renderlo il più portabile possibile in quanto è un programma open source utilizzato da molte persone. Per questo motivo, ho pensato che sarebbe stato meglio avvolgere il programma in un livello C++ in modo da sfruttare le librerie boost portatili. L'ho già fatto e tutto sembra funzionare come previsto.Passaggio di messaggi C++ multi-threaded

Il problema che sto avendo è decidere quale sia l'approccio migliore per passare i messaggi tra i thread. Fortunatamente, l'architettura del programma è quella di un produttore multiplo e di un singolo consumatore. Ancora meglio, l'ordine dei messaggi non è importante. Ho letto che le code single-producer/single-consumer (SPSC) trarrebbero beneficio da questa architettura. Quelli con esperienza di programmazione multi-threaded hanno qualche consiglio? Sono abbastanza nuovo per questa roba. Sarebbe molto apprezzato anche qualsiasi esempio di codice che utilizza boost per implementare SPSC.

+0

Vedere risposta accettata a http://stackoverflow.com/questions/8918401/does-a-multiple-producer-single-consumer-lock-free-queue-exist-for-c – walrii

risposta

7

Di seguito è la tecnica che ho utilizzato per la mia libreria Cooperative Multi-tasking/Multi-threading (MACE) http://bytemaster.github.com/mace/. Ha il vantaggio di essere privo di blocco tranne quando la coda è vuota.

+0

"Cooperativo": (( –

+0

.. anche se +1 per l'utilizzo di un collegamento all'interno di ciascun messaggio per evitare la memorizzazione dei messaggi all'interno della coda. –

+0

Un grazie fantastico – grouma

1

Se esiste un solo consumatore ma più produttori, quindi utilizzerei un array o una struttura di dati di tipo array con O (1) tempo di accesso in cui ogni slot di matrice rappresenta una coda di singolo produttore-consumatore. Il grande vantaggio di una coda single-producer-consumer è il fatto che è possibile renderlo lock-free senza meccanismi di sincronizzazione espliciti, rendendo così una struttura dati molto veloce in un ambiente multi-thread. Vedere my answer here per un'implementazione bare-bone di una coda single-producer-consumer.

+0

Ho usato questa tecnica prima di adottare il soluzione atomica di seguito. Il problema che avevo era che non si ridimensionava, consumava memoria mentre era inattivo per i buffer, e se non sai quali thread potrebbero essere pubblicati in anticipo (comune), devi ridimensionarli dinamicamente (tramite un lock) o hardcode con un conteggio massimo "grande". – bytemaster

+0

Stavo pensando di utilizzare una coda circolare ... in questo modo non è necessario ridimensionare la coda. – Jason

+0

che è la 'coda di dimensioni fisse' che può essere riempita, ma se si hanno N thread allora ogni thread ha bisogno di un input per tutti gli altri N thread, oppure la prima volta che un nuovo thread tenta di comunicare con un altro deve allocare il proprio singolo produttore/singola coda di consumo. Questo è il problema del ridimensionamento rispetto al codice duro. – bytemaster

1

Esistono molti esempi di code produttore-consumatore in rete, sicure per più produttori/consumatori. @bytemaster ha pubblicato uno che utilizza un collegamento all'interno di ciascun messaggio per eliminare lo spazio di archiviazione nella stessa classe della coda: questo è un buon approccio, lo utilizzo io stesso sui processi incorporati.

Laddove la classe di coda deve fornire spazio di archiviazione, di solito vado con una 'coda di riserva' di dimensione N, caricata con istanze di classe di messaggio N * all'avvio. I thread che devono comunicare devono inserire un messaggio * dal pool, caricarlo e accodarlo. Quando alla fine 'esaurito' il messaggio * viene reimmesso nella piscina. Ciò limita il numero di messaggi e quindi tutte le code devono essere solo di lunghezza N - nessun ridimensionamento, nessuna nuova(), nessuna cancellazione(), facile rilevamento delle perdite.

+0

Ho semplificato il codice per la risposta, ma in realtà ogni 'compito' sta memorizzando un funtore di 'dimensione sconosciuta' (per evitare l'allocazione dell'heap di boost :: function <>). Inoltre, ho lasciato il compito raddoppiato come una promessa contata di riferimento che è mantenuta viva fino a quando un futuro la mantiene. Finisco con 1 malloc per attività e posso spingere 2.4 milioni post di sincronizzazione/attesa al secondo. – bytemaster

Problemi correlati