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).