È vagamente correlato a questa domanda: std :: thread è in pool in C ++ 11? . Sebbene la domanda sia diversa, l'intenzione è la stessa:
Domanda 1: ha ancora senso utilizzare pool di thread propri (o di una libreria di terze parti) per evitare la creazione di thread costosi?
La conclusione nell'altra domanda era che non puoi fare affidamento std::thread
per essere messo in comune (potrebbe o potrebbe non esserlo). Tuttavia, std::async(launch::async)
sembra avere una probabilità molto più alta di essere raggruppati.
Non penso che sia forzato dallo standard, ma IMHO mi aspetterei che tutte le buone implementazioni C ++ 11 utilizzerebbero il pool di thread se la creazione del thread è lenta. Solo su piattaforme in cui è poco costoso creare un nuovo thread, mi aspetto che generino sempre un nuovo thread.
Domanda 2: Questo è proprio quello che penso, ma non ho fatti per dimostrarlo. Potrei benissimo sbagliarmi. È un'ipotesi plausibile?
Infine, qui ho fornito un codice di esempio che mostra prima come penso che la creazione di thread possa essere espressa da async(launch::async)
:
Esempio 1:
thread t([]{ f(); });
// ...
t.join();
diventa
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
Esempio 2: spara e dimentica il thread
thread([]{ f(); }).detach();
diventa
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
Domanda 3: Preferiresti le async
versioni alle thread
versioni?
Il resto non fa più parte della domanda, ma solo per chiarimenti:
Perché il valore restituito deve essere assegnato a una variabile fittizia?
Sfortunatamente, l'attuale standard C ++ 11 impone di catturare il valore di ritorno std::async
, altrimenti viene eseguito il distruttore, che si blocca fino al termine dell'azione. È da alcuni considerato un errore nello standard (ad esempio, da Herb Sutter).
Questo esempio tratto da cppreference.com lo illustra bene:
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
Un'altra precisazione:
So che i pool di thread possono avere altri usi legittimi, ma in questa domanda sono interessato solo all'aspetto di evitare costosi costi di creazione di thread .
Penso che ci siano ancora situazioni in cui i pool di thread sono molto utili, soprattutto se è necessario un maggiore controllo sulle risorse. Ad esempio, un server potrebbe decidere di gestire solo un numero fisso di richieste contemporaneamente per garantire tempi di risposta rapidi e aumentare la prevedibilità dell'utilizzo della memoria. I pool di thread dovrebbero andare bene, qui.
Le variabili locali del thread possono anche essere un argomento per i tuoi pool di thread, ma non sono sicuro che sia rilevante nella pratica:
- Creazione di un nuovo thread con
std::thread
inizi senza variabili locali del thread inizializzate. Forse questo non è quello che vuoi. - Nei thread generati da
async
, non è chiaro per me perché il thread avrebbe potuto essere riutilizzato. Dalla mia comprensione, non è garantito che le variabili locali del thread vengano reimpostate, ma potrei sbagliarmi. - L'utilizzo di pool di thread (a dimensione fissa), d'altra parte, ti dà il pieno controllo se ne hai davvero bisogno.
std::async()
. Sono ancora curioso di vedere come supportano distruttori thread_local non banali in un pool di thread.
launch::async
, la tratta come se fosse solo launch::deferred
e non la esegue mai in modo asincrono, quindi in effetti, quella versione di libstdc ++ "sceglie" utilizzare sempre differito, a meno che non sia costretto diversamente.
std::async
avrebbe potuto essere una cosa meravigliosa per le prestazioni: avrebbe potuto essere il sistema di esecuzione di attività a esecuzione breve standard, naturalmente supportato da un pool di thread. In questo momento, è solo un std::thread
con qualche schifezza appiccicata per rendere la funzione thread in grado di restituire un valore. Oh, e hanno aggiunto funzionalità ridondanti "differite" che si sovrappongono std::function
completamente al lavoro di .
std::async(launch::async)
sembra avere una probabilità molto più alta di essere raggruppati". No, credostd::async(launch::async | launch::deferred)
che possa essere messo insieme. Con sololaunch::async
l'attività dovrebbe essere avviata su un nuovo thread indipendentemente dalle altre attività in esecuzione. Con la politica,launch::async | launch::deferred
l'implementazione può scegliere quale politica, ma soprattutto può ritardare la scelta di quale politica. Ovvero, può attendere fino a quando un thread in un pool di thread non diventa disponibile e quindi scegliere il criterio asincrono.