Il est vaguement lié à cette question: std :: thread est-il regroupé en C ++ 11? . Bien que la question diffère, l'intention est la même:
Question 1: Est-il toujours judicieux d'utiliser vos propres pools de threads (ou ceux d'une bibliothèque tierce) pour éviter la création de threads coûteux?
La conclusion de l'autre question était que vous ne pouvez pas compter sur la std::thread
mise en commun (cela pourrait ou non). Cependant, std::async(launch::async)
semble avoir une chance beaucoup plus élevée d'être mis en commun.
Il ne pense pas qu'il soit forcé par la norme, mais à mon humble avis, je m'attendrais à ce que toutes les bonnes implémentations C ++ 11 utilisent le regroupement de threads si la création de threads est lente. Uniquement sur les plates-formes où il est peu coûteux de créer un nouveau fil, je m'attendrais à ce qu'ils génèrent toujours un nouveau fil.
Question 2: C'est exactement ce que je pense, mais je n'ai aucun fait pour le prouver. Je peux très bien me tromper. Est-ce une supposition éclairée?
Enfin, j'ai fourni ici un exemple de code qui montre d'abord comment je pense que la création de threads peut être exprimée par async(launch::async)
:
Exemple 1:
thread t([]{ f(); });
// ...
t.join();
devient
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
Exemple 2: déclencher et oublier le fil
thread([]{ f(); }).detach();
devient
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
Question 3: Préférez-vous les async
versions aux thread
versions?
Le reste ne fait plus partie de la question, mais uniquement pour clarification:
Pourquoi la valeur de retour doit-elle être affectée à une variable factice?
Malheureusement, le standard C ++ 11 actuel oblige à capturer la valeur de retour std::async
, sinon le destructeur est exécuté, ce qui se bloque jusqu'à ce que l'action se termine. C'est par certains considéré comme une erreur dans la norme (par exemple, par Herb Sutter).
Cet exemple de cppreference.com l' illustre bien:
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
Une autre précision:
Je sais que les pools de threads peuvent avoir d'autres utilisations légitimes, mais dans cette question, je ne suis intéressé que par l'aspect d'éviter les coûts de création de thread coûteux .
Je pense qu'il existe encore des situations où les pools de threads sont très utiles, surtout si vous avez besoin de plus de contrôle sur les ressources. Par exemple, un serveur peut décider de ne traiter qu'un nombre fixe de demandes simultanément pour garantir des temps de réponse rapides et augmenter la prévisibilité de l'utilisation de la mémoire. Les pools de threads devraient être bien, ici.
Les variables locales de thread peuvent également être un argument pour vos propres pools de threads, mais je ne suis pas sûr que cela soit pertinent dans la pratique:
- La création d'un nouveau thread avec
std::thread
démarre sans variables locales de thread initialisées. Ce n'est peut-être pas ce que vous voulez. - Dans les threads générés par
async
, ce n'est pas clair pour moi car le thread aurait pu être réutilisé. D'après ce que je comprends, les variables locales de thread ne sont pas garanties d'être réinitialisées, mais je peux me tromper. - L'utilisation de vos propres pools de threads (de taille fixe), en revanche, vous donne un contrôle total si vous en avez vraiment besoin.
std::async()
. Je suis toujours curieux de voir comment ils prennent en charge les destructeurs thread_local non triviaux dans un pool de threads.
launch::async
elle la traite comme si elle était seulement launch::deferred
et ne l'exécute jamais de manière asynchrone - donc en fait, cette version de libstdc ++ "choisit" à toujours utiliser différé sauf indication contraire.
std::async
aurait pu être une belle chose pour les performances - cela aurait pu être le système standard d'exécution de tâches à court terme, naturellement soutenu par un pool de threads. Pour le moment, c'est juste un std::thread
avec des conneries pour que la fonction de thread puisse renvoyer une valeur. Oh, et ils ont ajouté des fonctionnalités redondantes «différées» qui chevauchent std::function
complètement le travail .
std::async(launch::async)
semble avoir une chance beaucoup plus élevée d'être mis en commun." Non, je croisstd::async(launch::async | launch::deferred)
que c'est peut-être mis en commun. Avec justelaunch::async
la tâche est censée être lancée sur un nouveau thread indépendamment des autres tâches en cours d'exécution. Avec la politique,launch::async | launch::deferred
la mise en œuvre peut alors choisir quelle politique, mais plus important encore, elle retarde le choix de la politique. Autrement dit, il peut attendre qu'un thread d'un pool de threads devienne disponible, puis choisir la stratégie asynchrone.