web-dev-qa-db-fra.com

Comment une fonction peut-elle s'exécuter "comme si" sur un nouveau thread sans le faire?

Selon [futures.async]/3 puce 1 de la norme C++, lorsqu'une fonction f est transmise à std::async avec la stratégie de lancement std::launch::async, f sera exécuté "comme dans un nouveau thread d'exécution". 

Étant donné que f peut tout faire, y compris la boucle infinie et le blocage indéfini, comment une implémentation peut-elle offrir le comportement de f s'exécutant sur son propre thread sans l'exécuter réellement sur son propre thread? Autrement dit, comment une implémentation peut-elle tirer parti de la marge de manœuvre "comme si" offerte par la norme?

19

En regardant dans les références C++, comme cela apparaît ici et ici , il est {semble) que le but "comme si" est de laisser un certain degré de liberté au réalisateur de la bibliothèque. Par exemple, il est dit 

comme si engendré par std :: thread (std :: forward (f), std :: forward (args) ...), sauf que si la fonction f renvoie un valeur ou lève une exception, elle est stockée à l'état partagé accessible via le std :: future qui async retourne à l'appelant.

dans la première source, et 

comme si un objet thread était construit avec fn et args comme arguments, et accéder à l'état partagé du futur retourné le rejoint

dans la seconde. Il semble donc que le comportement soit similaire à celui de std::thread mais que son implémentation soit différente. Ceci est intéressant, car la phrase fil d’exécution que vous citez ici se distingue de std::thread. Pourtant, il semble que les deux sources comprennent le comme-si de cette façon.

Une autre option pourrait être, comme suggéré par François Andrieux de permettre l’utilisation de threadpool, comme indiqué dans la première source:

La fonction modèle async exécute la fonction f de manière asynchrone (potentiellement dans un thread séparé pouvant faire partie d'un pool de threads)

6
Mike

Si je pouvais penser que f pourrait fonctionner "comme si" sur un nouveau thread sans le faire, ce serait le cas si f n'utilisait en réalité aucun état partagé avec d'autres threads; alors l'implémentation pourrait l'exécuter en tant que processus séparé (car elle ne nécessite pas d'espace mémoire partagé), elle pourrait également simplement l'exécuter sur le thread principal en tant qu'appel de fonction supplémentaire (si elle peut prouver que f ne bloque pas ou n'a pas d'observable effets secondaires qui diffèrent lorsqu’ils sont exécutés de cette façon). Il pourrait également être planifié pour s'exécuter sur un thread existant (mais inactif) (pool de threads).

Et si vous voulez être idiot, vous pouvez également envisager la possibilité de ne pas exécuter fdu tout} _, car il n'y a aucune garantie quant au moment où de nouveaux threads seront planifiés pour s'exécuter par un système Une implémentation maléfique pourrait simplement dire que le système d’exploitation ne planifie jamais de thread, à l’exception du thread principal. Par conséquent, ne pas exécuter f équivaut à le programmer sur un nouveau thread. Bien sûr, cela est stupide/stupide et aucune mise en œuvre saine ne le ferait jamais - mais en théorie le langage permet une mise en œuvre aussi dégénérée (je pense).

5
Jesper Juhl

L'avantage de cette solution est que vous avez optimisé l'implémentation pour un rôle spécifique dans le monde du multi-threading. Comme je suis impliqué dans les procédures de mise à jour logicielle, je vais utiliser cet exemple ..__ Pour des raisons d'optimisation, vous pouvez appeler:

std::thread::hardware_concurrency();

Vous allez recevoir:

Retourne le nombre de threads simultanés supportés par le la mise en oeuvre.

Supposons que vous obteniez un résultat égal à 4 . Vous souhaitez effectuer la mise à jour de nombreuses choses en parallèle . Le fil principal surveille la liste des contrats à terme (il en reste 3) et vérifie de temps en temps si c'est fait ou non et si c'est fait, exécutez prochaine chose de la liste ToDo. Le profit ici est par exemple si vous mettez à jour différents types de mémoire comme 1-FileSystem, 2-EEPROM, 3-NOR ou quelque chose du genre . Il n'y a aucun avantage à vérifier en boucle sans délai, vous voulez donc donner une tâche à 4-ème thread entre les contrôles . Vous écrivez une fonction qui creuse des bitcoins pendant 50 millisecondes:) .__ et vous le déclenche comme différé entre les contrôles.

Et puis, comme vous l'avez mentionné, nous avons:

avantage de la marge de manœuvre "comme si" proposée par la norme

5

Autant que je sache, le runtime C++ est capable de gérer certains threads internal (ou spécialisés), distincts des threads standard.

Mon hypothèse (j'apprends toujours les fonctionnalités avancées de C++) est que, avec l'indicateur std::launch::async, la std::async() lancera f dans un nouveau thread internal.

Vous pouvez lancer f dans un nouveau thread standard en utilisant std::thread. Dans ce cas, les exceptions seront gérées sur le thread appelé et le code principal devra extraire la valeur de retour de f.

Avec le thread internal, la valeur de retour et l'exception éventuelle sont stockées dans std::future, renvoyées par std::async() et partagées entre les threads.

Donc "comme si" signifie "comme si je lance f avec std::thread mais je n'ai pas à le faire". Mais pour sûr, f fonctionnera sur un nouveau thread.

Pour répondre à votre question sur l'implémentation, une fois que le thread d'exécution a déjà implémenté les threads standard, vous devez simplement vous efforcer de les spécialiser pour des utilisations spécifiques. Voir aussi la politique execution pour les algorithmes prenant en charge la parallélisation .

2
edixon

Je vois l'idée principale de "f s'exécutera" comme si dans un nouveau thread d'exécution "" est-ce que f s'exécutera de manière asynchrone. Que ce soit un nouveau fil ou autre chose, ce sont les détails de l'implémentation.

2
wtom

La seule solution sans Thread, à laquelle je puisse penser, est le découpage temporel d'hier, qui est une forme de multitâche. Les fonctions doivent être rendues de nouveau. Dans un tel scénario, chaque fonction s'exécutera comme si elle se trouvait sur différents threads, bien qu'elle se trouve dans la pratique sur un seul thread.

1
seccpur

Dans la documentation, il y était indiqué:

La fonction de modèle async exécute la fonction f de manière asynchrone (potentiellement dans un thread séparé pouvant faire partie d'un pool de threads) et renvoie un std :: future qui contiendra éventuellement le résultat de cet appel de fonction. 

http://fr.cppreference.com/w/cpp/thread/async

0
Truth