Quand dois-je utiliser std :: thread :: detach?


140

Parfois, je dois utiliser std::threadpour accélérer mon application. Je sais aussi qu'il join()attend la fin d'un fil. C'est facile à comprendre, mais quelle est la différence entre appeler detach()et ne pas l'appeler?

Je pensais que sans detach(), la méthode du thread fonctionnera en utilisant un thread indépendamment.

Ne pas se détacher:

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

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

    //some code here
}

Appel avec détachement:

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

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

    t.detach();

    //some code here
}


Les deux stdet les boostthreads ont detachet ont joinmodelé de près les threads POSIX.
n. «pronoms» m.

Réponses:


149

Dans le destructeur de std::thread, std::terminateest appelé si:

  • le fil n'a pas été joint (avec t.join())
  • et n'était pas détaché non plus (avec t.detach())

Ainsi, vous devez toujours soit joinou detachun thread avant que les flux d'exécution n'atteignent le destructeur.


Lorsqu'un programme se termine (c'est-à-dire qu'il mainretourne), les threads détachés restants s'exécutant en arrière-plan ne sont pas attendus; au lieu de cela, leur exécution est suspendue et leurs objets locaux de thread détruits.

Fondamentalement, cela signifie que la pile de ces threads n'est pas déroulée et que certains destructeurs ne sont donc pas exécutés. Selon les actions que ces destructeurs étaient censés entreprendre, cela pourrait être une situation aussi grave que si le programme s'était écrasé ou avait été tué. Espérons que le système d'exploitation libérera les verrous sur les fichiers, etc., mais vous pourriez avoir corrompu la mémoire partagée, les fichiers à moitié écrits, etc.


Alors, devriez-vous utiliser joinou detach?

  • Utilisation join
  • À moins que vous n'ayez besoin de plus de flexibilité ET que vous soyez prêt à fournir un mécanisme de synchronisation pour attendre la fin du thread par vous-même , auquel cas vous pouvez utiliserdetach

Si j'appellerais pthread_exit (NULL); dans main () alors exit () ne serait pas appelé depuis main () et donc le programme continuera l'exécution jusqu'à ce que tous les threads détachés soient terminés. Ensuite, exit () serait appelé.
southerton

1
@Matthieu, pourquoi ne pouvons-nous pas rejoindre le destructeur de std :: thread?
john smith

2
@johnsmith: Une excellente question! Que se passe-t-il lorsque vous vous inscrivez? Vous attendez que le thread se termine. Si une exception est levée, les destructeurs sont exécutés ... et soudainement la propagation de votre exception est suspendue jusqu'à ce que le thread se termine. Il y a de nombreuses raisons pour lesquelles il ne le fait pas, notamment s'il attend une entrée du thread actuellement suspendu! Les concepteurs ont donc choisi d'en faire un choix explicite, plutôt que de choisir un défaut controversé.
Matthieu M.

@Matthieu Je pense que vous voulez dire appeler join () avant que le destructeur de std :: thread ne soit atteint. Vous pouvez (et devriez?) Joindre () le destructeur d'une classe englobante?
Jose Quinteiro

4
@JoseQuinteiro: En fait, contrairement à d'autres ressources, il est conseillé de ne pas rejoindre depuis un destructeur. Le problème est que la jointure ne termine pas un thread, elle attend simplement qu'il se termine, et contrairement à ce que vous avez un signal en place pour provoquer la fin du thread, vous pourriez attendre longtemps ... bloquant le thread actuel dont la pile est en cours de déroulement et empêche ce thread actuel de se terminer, bloquant ainsi le thread qui l'attend, etc ... Donc, à moins que vous ne soyez certain de pouvoir arrêter un thread donné dans un laps de temps raisonnable, il vaut mieux ne pas attendre dans un destructeur.
Matthieu M.

25

Vous devriez appeler detachsi vous n'attendez pas que le thread se termine, joinmais le thread continuera à fonctionner jusqu'à ce que ce soit terminé, puis se terminera sans que le thread principal l'attende spécifiquement.

detachva essentiellement libérer les ressources nécessaires pour être en mesure de mettre en œuvre join.

C'est une erreur fatale si un objet thread termine sa vie et que ni joinni detachn'a été appelé; dans ce cas terminateest invoquée.


12
Vous devez mentionner que terminate est appelé dans le destructeur, si le thread n'a été ni joint ni détaché .
nosid

11

Lorsque vous détachez le fil, cela signifie que vous n'êtes pas obligé de le join()faire avant de quitter main().

La bibliothèque de threads attendra chaque thread ci-dessous , mais vous ne devriez pas vous en soucier.

detach()est principalement utile lorsque vous avez une tâche qui doit être effectuée en arrière-plan, mais que vous ne vous souciez pas de son exécution. C'est généralement le cas pour certaines bibliothèques. Ils peuvent créer silencieusement un thread de travail en arrière-plan et le détacher afin que vous ne le remarquiez même pas.


Cela ne répond pas à la question. La réponse indique essentiellement "vous vous détachez lorsque vous vous détachez".
rubenvb

7

Cette réponse vise à répondre à la question dans le titre, plutôt qu'à expliquer la différence entre joinet detach. Alors, quand faut- std::thread::detachil l'utiliser?

Dans le code C ++ correctement entretenu, std::thread::detachne doit pas du tout être utilisé. Le programmeur doit s'assurer que tous les threads créés se terminent correctement en libérant toutes les ressources acquises et en exécutant les autres actions de nettoyage nécessaires. Cela implique que renoncer à la propriété des threads en appelant detachn'est pas une option et joindoit donc être utilisé dans tous les scénarios.

Cependant, certaines applications reposent sur des API anciennes et souvent mal conçues et prises en charge qui peuvent contenir des fonctions de blocage indéfiniment. Déplacer les invocations de ces fonctions dans un thread dédié pour éviter de bloquer d'autres éléments est une pratique courante. Il n'y a aucun moyen de faire en sorte qu'un tel thread se termine correctement, donc l'utilisation de joinmènera simplement au blocage du thread principal. C'est une situation dans laquelle l'utilisation detachserait une alternative moins mauvaise, par exemple, à l'allocation d'un threadobjet avec une durée de stockage dynamique, puis à le divulguer intentionnellement.

#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

Selon cppreference.com :

Sépare le thread d'exécution de l'objet thread, ce qui permet à l'exécution de se poursuivre indépendamment. Toutes les ressources allouées seront libérées une fois le thread terminé.

Après avoir appelé detach, *thisne possède plus de thread.

Par exemple:

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

Remarquez la variable locale:, tant my_threadque la durée de vie de my_threadest terminée, le destructeur de std::threadsera appelé, et std::terminate()sera appelé dans le destructeur.

Mais si vous utilisez detach(), vous ne devriez plus utiliser my_thread, même si la durée de vie de my_threadest terminée, rien n'arrivera au nouveau thread.


OK, je reprends ce que j'ai dit tout à l'heure. @ TobySpeight
DinoStray

1
Notez que si vous utilisez &dans la capture lambda, vous utilisez les variables de la portée englobante par référence - vous feriez donc mieux de vous assurer que la durée de vie de tout ce que vous référencez est plus longue que la durée de vie de votre thread.
DavidJ
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.