Quando dovrei usare std :: thread :: detach?


140

A volte devo usare std::threadper velocizzare la mia applicazione. So anche che join()aspetta che un thread sia completato. È facile da capire, ma qual è la differenza tra chiamare detach()e non chiamarlo?

Ho pensato che senza detach(), il metodo del thread funzionerà usando un thread in modo indipendente.

Non staccare:

void Someclass::Somefunction() {
    //...

    std::thread t([ ] {
        printf("thread called without detach");
    });

    //some code here
}

Chiamata con distacco:

void Someclass::Somefunction() {
    //...

    std::thread t([ ] {
        printf("thread called with detach");
    });

    t.detach();

    //some code here
}


Entrambi stde boostthread hanno detache joinmodellato da vicino i thread POSIX.
n. 'pronomi' m.

Risposte:


149

Nel distruttore di std::thread, std::terminateviene chiamato se:

  • il thread non è stato unito (con t.join())
  • e non è stato staccato neanche (con t.detach())

Quindi, dovresti sempre uno joino detachun thread prima che i flussi di esecuzione raggiungano il distruttore.


Quando un programma termina (cioè mainrestituisce) i rimanenti thread separati in esecuzione in background non vengono attesi; invece la loro esecuzione è sospesa e i loro oggetti thread-local distrutti.

Fondamentalmente, questo significa che la pila di quei thread non è srotolata e quindi alcuni distruttori non vengono eseguiti. A seconda delle azioni che quei distruttori avrebbero dovuto intraprendere, questa potrebbe essere una brutta situazione come se il programma fosse andato in crash o fosse stato ucciso. Si spera che il sistema operativo rilascerà i blocchi sui file, ecc ... ma potresti aver danneggiato la memoria condivisa, i file scritti a metà e simili.


Quindi, dovresti usare joino detach?

  • Uso join
  • A meno che tu non abbia bisogno di maggiore flessibilità E sia disposto a fornire un meccanismo di sincronizzazione per attendere il completamento del thread da solo , nel qual caso puoi usaredetach

Se chiamassi pthread_exit (NULL); in main () quindi exit () non verrebbe chiamato da main () e quindi il programma continuerà l'esecuzione fino al completamento di tutti i thread separati. Quindi verrà chiamato exit ().
southerton,

1
@Matthieu, perché non possiamo unirci al distruttore di std :: thread?
John Smith,

2
@johnsmith: un'ottima domanda! Cosa succede quando ti iscrivi? Attendi il completamento del thread. Se viene generata un'eccezione, i distruttori vengono eseguiti ... e improvvisamente la propagazione dell'eccezione viene sospesa fino al termine del thread. Ci sono molte ragioni per cui non lo è, in particolare se è in attesa di input dal thread attualmente sospeso! Quindi i designer hanno scelto di renderlo una scelta esplicita, piuttosto che scegliere un valore controverso.
Matthieu M.,

@Matthieu Penso che intendi chiamare join () prima che venga raggiunto il distruttore di std :: thread. Puoi (e dovresti?) Unire () il distruttore di una classe chiusa?
Jose Quinteiro,

4
@JoseQuinteiro: In realtà, a differenza di altre risorse, si consiglia di non unirsi a un distruttore. Il problema è che l'unione non termina un thread, ma semplicemente aspetta che finisca e, diversamente da un segnale in atto per far terminare il thread, potresti aspettare molto tempo ... bloccando il thread corrente il cui stack viene srotolato e impedisce che questo thread corrente si interrompa, bloccando così il thread in attesa di esso, ecc. Quindi, a meno che non si sia certi di poter interrompere un determinato thread in un ragionevole lasso di tempo, è meglio non attendere in un distruttore.
Matthieu M.,

25

Dovresti chiamare detachse non stai aspettando che il thread venga completato, joinma il thread continuerà invece a essere eseguito fino a quando non sarà terminato e quindi terminerà senza che il thread principale lo attenda in modo specifico.

detachfondamentalmente rilascerà le risorse necessarie per essere in grado di implementare join.

Si tratta di un errore fatale se un oggetto thread termina la sua vita e né joindetachè stato chiamato; in questo caso terminateviene invocato.


12
Dovresti menzionare che terminare viene chiamato nel distruttore, se il thread non è stato né unitostaccato .
nosid

11

Quando scolleghi il thread significa che non devi farlo join()prima di uscire main().

La libreria di thread attenderà effettivamente ciascuno di questi thread sotto-main , ma non dovresti preoccupartene.

detach()è utile soprattutto quando hai un'attività che deve essere eseguita in background, ma non ti importa della sua esecuzione. Questo di solito è un caso per alcune biblioteche. Potrebbero creare silenziosamente un thread di lavoro in background e staccarlo in modo da non notarlo nemmeno.


Questo non risponde alla domanda. La risposta afferma sostanzialmente "ti stacchi quando ti stacchi".
rubenvb,

7

Questa risposta ha lo scopo di rispondere alla domanda nel titolo, piuttosto che spiegare la differenza tra joine detach. Quindi, quando std::thread::detachdovrebbe essere usato?

Nel codice C ++ correttamente gestito std::thread::detachnon dovrebbe essere usato affatto. Il programmatore deve assicurarsi che tutti i thread creati escano con grazia rilasciando tutte le risorse acquisite ed eseguendo le altre azioni di pulizia necessarie. Ciò implica che rinunciare alla proprietà dei thread invocando detachnon è un'opzione e pertanto joindovrebbe essere utilizzata in tutti gli scenari.

Tuttavia, alcune applicazioni si basano su API vecchie e spesso non ben progettate e supportate che possono contenere funzioni di blocco indefinito. Spostare le invocazioni di queste funzioni in un thread dedicato per evitare di bloccare altre cose è una pratica comune. Non c'è modo di fare in modo che un thread del genere esca con garbo, quindi l'uso di joinporterà solo al blocco del thread primario. Questa è una situazione in cui l'utilizzo detachsarebbe un'alternativa meno malvagia, per esempio, allocare un threadoggetto con una durata di memorizzazione dinamica e poi trapelare di proposito.

#include <LegacyApi.hpp>
#include <thread>

auto LegacyApiThreadEntry(void)
{
    auto result{NastyBlockingFunction()};
    // do something...
}

int main()
{
    ::std::thread legacy_api_thread{&LegacyApiThreadEntry};
    // do something...
    legacy_api_thread.detach();
    return 0;
}

1

Secondo cppreference.com :

Separa il thread di esecuzione dall'oggetto thread, consentendo l'esecuzione in modo indipendente. Eventuali risorse allocate verranno liberate una volta terminato il thread.

Dopo aver chiamato staccare *thisnon possiede più alcun thread.

Per esempio:

  std::thread my_thread([&](){XXXX});
  my_thread.detach();

Notare la variabile locale:, my_threadmentre la durata di my_threadè terminata, std::threadverrà chiamato il distruttore di e std::terminate()verrà chiamato all'interno del distruttore.

Ma se usi detach(), non dovresti my_threadpiù usare , anche se la durata di vita my_threadè finita, non succederà nulla al nuovo thread.


OK, riprendo ciò che ho detto proprio ora. @ TobySpeight
DinoStray

1
Nota che se usi &nella cattura lambda stai usando le variabili dell'ambito che racchiude per riferimento , quindi assicurati che la durata di qualsiasi riferimento sia più lunga della durata del thread.
DavidJ,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.