Non appena si avvia l'applicazione, e (si spera) prima che sia effettivamente necessario chiamare lo get_X()
, chiamarla gratuitamente. Inoltre, in modo che la fase di inizializzazione sia più veloce, invia le tue costose inizializzazioni a thread diversi. Ad esempio:
#include <thread>
int
main()
{
std::thread(get_X).detach();
// continue with other initialization...
}
Quando qualche compito è costoso come diverse centinaia di millisecondi (o più), il sovraccarico di scorporo un filo da affrontare è nel livello di rumore. E se siete su hardware multi-core (cosa non è in questi giorni?), Allora si tratta di una vittoria chiara delle prestazioni se la vostra applicazione non ha realmente bisogno di nulla da questo singleton fino a quando non viene completata la chiamata iniziale a get_X
.
Note/Domande:
Perché detach
il thread
? Perché non join
?
Se si decide di join
questo thread
, significa che devi solo aspettare che finisca, perché non fare altre cose invece. Al termine, detach
ha ripulito se stesso. Non è nemmeno necessario mantenere un handle per lo thread
. Le distruzioni temporanee di std::thread
, ma il thread del sistema operativo è attivo, con il completamento di get_X
.
Quando thread
veniva standardizzato, c'erano punti di vista che detach
ed thread
s non erano solo inutili, ma pericolosi. Ma ecco un caso d'uso perfettamente sicuro e abbastanza motivante per detach
ed thread
s.
E se la mia applicazione chiama get_X()
prima della detach
ndr thread
finiture la prima chiamata a get_X()
?
C'è un colpo di prestazioni, ma non un colpo di correttezza. L'applicazione si blocca a questa linea in get_X()
:
static const X x = init_X();
fino alla detach
ndr thread
è terminato l'esecuzione di esso. Quindi non esiste una gara di dati.
Cosa succede se la mia domanda termina prima che il detach
e thread
sia completo?
Se l'applicazione termina durante la fase di inizializzazione, qualcosa è evidentemente andato male catastroficamente.Se get_X
tocca qualcosa che è già stato distrutto dalla catena at_exit
(che viene eseguita dopo main), accadranno cose brutte. Tuttavia, si è già in uno stato di arresto di emergenza ... un'ulteriore emergenza non è in grado di peggiorare la situazione di panico. Sei già morto. Otoh, se la tua inizializzazione è qualcosa che richiede minuti a ore, probabilmente do hanno bisogno di una comunicazione migliore su quando la tua inizializzazione è completata e procedure di spegnimento più aggraziate. In tal caso è necessario implementare la cancellazione cooperativa nelle discussioni, distaccate o meno (una cosa che la commissione std ha rifiutato di fornirti).
Cosa succede se la prima chiamata a get_X()
genera un'eccezione?
In questo caso, la seconda chiamata a get_X()
ha la possibilità di inizializzare la funzione locale statica, presupponendo di non lasciare l'eccezione non rilevata e consentirgli di terminare il programma. Potrebbe anche buttare, o potrebbe riuscire nell'inizializzazione (cioè fino al tuo codice). In ogni caso, le chiamate a get_X()
continueranno a provare a inizializzarsi, in attesa se l'inizializzazione è in corso, fino a quando qualcuno non riuscirà a farlo senza generare un'eccezione. E questo è tutto vero indipendentemente dal fatto che le chiamate arrivino o no da thread diversi o meno.
In sintesi
std::thread(get_X).detach();
è un buon modo per sfruttare la potenza dei vostri più core per ottenere inizializzazioni costosi indipendenti fuori del modo il più rapidamente possibile, senza compromettere la correttezza thread-sicurezza.
L'unico svantaggio è che si inizializzano i dati entro get_X()
se ne avete bisogno o no. Quindi assicurati di averne bisogno prima di usare questa tecnica.
[Nota in calce] Per coloro che utilizzano Visual Studio, questo è una buona motivazione per passare a VS-2015. Prima di questa versione VS non implementa la funzione locale statica thread-safe.
fonte
2015-07-17 03:10:26
più uno, perché ho imparato qualcosa di nuovo. Beh, probabilmente ne avrei fatto un po 'dopo aver incontrato il problema. Ma, bello sapere. :) –
Se l'inizializzazione può essere lanciata, potrebbe essere necessario avvolgerla in un tentativo/catch per evitare la 'terminazione'? –
"non c'è traccia di dati" - purché si stia utilizzando un compilatore che ha effettivamente implementato la sicurezza del thread C++ 11 per l'inizializzazione statica della funzione locale. – TheUndeadFish