Supponiamo di avere una coda senza blocco in un'impostazione multithread. Fornisco già un metodo try_dequeue()
che consente un errore facoltativo (comunicato tramite il tipo restituito) se la coda è vuota.
Ho trovato comodo disporre di un metodo T dequeue()
sempre riuscito che non verrà restituito mentre la coda è vuota. Come consumatore, non mi interessa davvero quando ottengo il mio prodotto, voglio solo sapere non appena uno è disponibile.
La mia implementazione di dequeue()
è piuttosto semplice:
while (true) {
if (try_dequeue succeeded) return result;
std::unique_lock<std::mutex> lk(mtx_);
cond_.wait(lk, [this]{ return !empty(); });
}
Dove cond_
è segnalato da enqueuers e mtx_
guards cond_
.
La serializzazione in eccesso e la contesa tra i dequeuer causati da lk
sembrano rallentare le cose. empty()
sicuramente non ha bisogno di nulla da bloccare.
In ogni caso, cond_
sembra fuori luogo qui? L'unica ragione per cui esita a rimuoverlo è il caso in cui è presente un dequeuer (o più dequeuers) che attendono l'arrivo di un nuovo oggetto e ruotano pigramente sulla CPU senza alcuna indicazione che il thread di enqueuing abbia priorità.
Sembrerebbe che forse this_thread::yield()
sia più appropriato qui, ma lo standard garantisce solo che il metodo sia un suggerimento, quindi c'è una possibilità di morire di fame qui.
Sembra che boost :: lockfree :: queue non fornisce affatto un metodo dequeue
-like. Speriamo che l'opzione nucleare sia evitabile.
Come sottolinea l'utente, potremmo considerare una soluzione a questo problema come un approccio "ibrido" o "semi-bloccante" perché sarebbe auspicabile avere blocchi di dequeuers su code vuote, ma sarebbe anche bello per gli enqueuers non dover fare contemporaneamente un cambio di contesto completo al kernel.
Nota: Sì, tecnicamente l'inclusione di mtx_
nella coda rimuove l'attributo lockfree, ma penso che questa sia comunque una caratterizzazione accurata poiché dequeue()
è l'unico metodo che usa mtx_
(segnale degli enqueuers senza blocco).