Une future est un objet représentant un éventuel résultat. Bien utilisée, une future peut accroître le parallélisme d'un programme.
Depuis C++ 11, il est possible d'utiliser des futures en C++. Un exemple simple simple comparant un calcul synchrone et un calcul équivalent mais réalisé de manière asynchrone, à l'aide de futures, suit.
Quelques en-têtes standards sont inclus. Nous utiliserons :
|
|
Nous utiliserons deux calculs très (hum) sophistiqués, soit les fonctions f() et g() qui s'exécuteront respectivement en environ une seconde et environ deux secondes. L'idée ici sera de montrer les gains possibles avec une approche de calcul asynchrone, tout en mettant en relief l'accroissement (!) de la complexité de la tâche que ce virage implique. Notez le recours aux littéraux 1s et 2s de type seconds. |
|
La fonction calcul_synchrone(n) calculera de manière synchrone. Le temps total requis pour exécuter ce calcul sera essentiellement la somme des temps requis pour exécuter et . La fonction calcul_asynchrone(n) calculera de manière asynchrone. Le temps total requis pour exécuter ce calcul sera essentiellement le maximum des temps requis pour exécuter et . Ici, f_ et g_ sont des futures; leur type est std::future<int>, car chacune d'elles encapsule un calcul qui retournera un éventuel int. Les calculs sont lancés par std::async(), qui accepte une opération et ses paramètres, puis effectue le calcul associé de manière asynchrone. La méthode get() bloque jusqu'à ce qu'un résultat devienne disponible, ou jusqu'à ce qu'une exception soit levée, et retourne ce résultat (ou re-lève cette exception, selon le cas). |
|
La fonction test() semble complexe, car elle repose sur des mécanismes variadiques, mais ce qu'il faut savoir d'elle est qu'elle prend une fonction , ses paramètres (il peut y en avoir plusieurs), puis calcule le temps requis pour exécuter . La fonction test() en tant que telle retourne une paire dont le premier élément est le résultat du calcul, et dont le second élément est le temps écoulé. En pratique, elle est très facile à utiliser (voir ci-dessous). Je profite pour la valeur de retour de CTAD, un mécanisme de C++ 17. Si votre compilateur n'est pas à jour, vous pouvez remplacer pair{...} par make_pair(...). |
|
Enfin, le programme de test calcule de manière synchrone, puis de manière asynchrone, et affiche à la fois les résultats (identiques) et le temps requis pour y arriver (différent, évidemment). J'utilise pour la valeur de retour les Structured Bindings de C++ 17, dans le but d'alléger l'écriture. |
|
Une exécution possible serait :
Calcul synchrone, resultat : 5 obtenu en 3000 ms.
Calcul asynchrone, resultat : 5 obtenu en 2003 ms.
Les futures avec C# sont implémentées à partir de fonctions dites « résumables », à l'aide des mots clés async et await. Pour en savoir plus, voir ../Divers--cdiese/async_await.html
Pour un exemple de future « maison », pré-C++ 11, par votre humble serviteur, voir ../Client-Serveur/futures-maison.html.
Évidemment, ces expérimentations sont amusantes mais ne remplacent pas les outils standards, que je vous invite à privilégier.
Un Wiki sur le sujet : http://en.wikipedia.org/wiki/Futures_and_promises
La position de Marius A. Eriksen en 2013 : http://aboutwhichmorelater.tumblr.com/post/46862769549/futures-arent-ersatz-threads
Les futures et les promesses avec iOS (mais au fond, ce texte ratisse beaucoup plus large et ne se limite pas à une plateforme particulière), texte de Drew Crawford en 2013 : http://sealedabstract.com/code/broken-promises/
En 2016, Raymond Chen montre comment il est possible de transformer une boucle en un enchaînement de tâches : https://blogs.msdn.microsoft.com/oldnewthing/20160226-00/?p=93092
Avec C# :
Avec C++ :
Bloquer ou non à la destruction?
Les futures générées par std::async() de C++ 11 bloquent à la destruction jusqu'à ce que le thread qui leur est associé complète sa tâche, un comportement qui, en surface du moins, diffère de celles créées par d'autres moyens. Ceci peut irriter les programmeuses et les programmeurs, et amène discussions et réflexions en vue de C++ 14 dans l'optique d'harmoniser les effets de ces diverses approches. |
|
À ce sujet :
Avec Go :
Avec Java :
Avec JavaScript :