web-dev-qa-db-fra.com

Qu'est-ce que std :: promise?

Je connais assez bien les composants std::thread, std::async et std::future de C++ 11 (par exemple, voyez cette réponse ), qui sont directs.

Cependant, je ne peux pas tout à fait comprendre ce que std::promise est ce qu'il fait et dans quelles situations il est le mieux utilisé. Le document standard lui-même ne contient pas beaucoup d'informations au-delà de son synopsis de classe, pas plus que just :: thread .

Quelqu'un pourrait-il donner un exemple succinct et succinct d'une situation dans laquelle un std::promise est nécessaire et constitue la solution la plus idiomatique?

359
Kerrek SB

Dans les mots de [futures.state] un std::future est un objet de retour asynchrone ("un objet qui lit les résultats d'un état partagé") et un std::promise est un - fournisseur asynchrone ("un objet qui fournit un résultat à un état partagé"), c’est-à-dire qu’une promesse est la chose sur laquelle vous définissez un résultat, afin que vous puissiez get il du futur associé.

Le fournisseur asynchrone est ce qui crée initialement l'état partagé auquel un futur fait référence. std::promise est un type de fournisseur asynchrone, std::packaged_task en est un autre et le détail interne de std::async en est un autre. Chacun de ceux-ci peut créer un état partagé et vous donner un std::future qui partage cet état et peut le rendre prêt.

std::async est un utilitaire de niveau supérieur qui vous fournit un objet de résultat asynchrone. Il prend en charge en interne la création du fournisseur asynchrone et la préparation de l'état partagé à la fin de la tâche. Vous pouvez l'émuler avec un std::packaged_task (ou std::bind et un std::promise) et un std::thread, mais il est plus sûr et plus facile d'utiliser std::async.

std::promise est un peu plus bas niveau, pour quand vous voulez transmettre un résultat asynchrone à l'avenir, mais le code qui le rend prêt ne peut pas être encapsulé dans une seule fonction appropriée pour passer à std::async . Par exemple, vous pouvez avoir un tableau de plusieurs promises et associés futures et avoir un seul thread qui effectue plusieurs calculs et définit un résultat sur chaque promesse. async ne vous permet que de renvoyer un seul résultat, vous devez en appeler plusieurs (async plusieurs fois, ce qui peut entraîner un gaspillage de ressources.

173
Jonathan Wakely

Je comprends un peu mieux la situation maintenant (en grande partie à cause des réponses fournies ici!), Alors j'ai pensé ajouter un petit article de ma part.


Il existe deux concepts distincts, quoique liés, dans C++ 11: le calcul asynchrone (une fonction appelée ailleurs) et l'exécution simultanée (un thread, quelque chose qui fonctionne simultanément). Les deux sont des concepts quelque peu orthogonaux. Le calcul asynchrone est simplement une variante différente de l'appel de fonction, alors qu'un thread est un contexte d'exécution. Les threads sont utiles en eux-mêmes, mais pour les besoins de cette discussion, je les traiterai comme un détail de la mise en œuvre.


Il existe une hiérarchie d'abstraction pour le calcul asynchrone. Par exemple, supposons que nous ayons une fonction qui prend quelques arguments:

_int foo(double, char, bool);
_

Tout d’abord, nous avons le modèle std::future<T> , qui représente une valeur future de type T. La valeur peut être récupérée via la fonction membre get(), qui synchronise efficacement le programme en attendant le résultat. Sinon, un futur supporte wait_for(), qui peut être utilisé pour déterminer si le résultat est déjà disponible ou non. Les contrats à terme doivent être considérés comme le remplacement instantané asynchrone des types de retour ordinaires. Pour notre exemple de fonction, nous attendons un _std::future<int>_.

Passons maintenant à la hiérarchie, du plus haut niveau au plus bas:

  1. std::async : La méthode la plus simple et la plus simple pour effectuer un calcul asynchrone consiste à utiliser le modèle de fonction async, qui renvoie immédiatement l'avenir correspondant:

    _auto fut = std::async(foo, 1.5, 'x', false);  // is a std::future<int>
    _

    Nous avons très peu de contrôle sur les détails. En particulier, nous ne savons même pas si la fonction est exécutée simultanément, en série sur get() ou par une autre magie noire. Cependant, le résultat est facilement obtenu en cas de besoin:

    _auto res = fut.get();  // is an int
    _
  2. Nous pouvons maintenant considérer comment implémenter quelque chose comme async, mais d'une manière que nous contrôlons. Par exemple, nous pouvons exiger que la fonction soit exécutée dans un thread séparé. Nous savons déjà que nous pouvons fournir un thread séparé au moyen de la classe std::thread .

    Le niveau d'abstraction inférieur suivant fait exactement cela: std::packaged_task . Il s'agit d'un modèle qui enveloppe une fonction et fournit un futur pour la valeur de retour des fonctions, mais l'objet lui-même est appelable et l'appel est à la discrétion de l'utilisateur. Nous pouvons le configurer comme ceci:

    _std::packaged_task<int(double, char, bool)> tsk(foo);
    
    auto fut = tsk.get_future();    // is a std::future<int>
    _

    Le futur devient prêt une fois l'appel lancé et l'appel terminé. C'est le travail idéal pour un thread séparé. Nous devons juste nous assurer que déplacez la tâche dans le fil:

    _std::thread thr(std::move(tsk), 1.5, 'x', false);
    _

    Le fil commence à courir immédiatement. Nous pouvons soit detach soit, soit join à la fin de la portée, ou à tout moment (par exemple, en utilisant le gestionnaire _scoped_thread_ d'Anthony Williams, qui devrait réellement figurer dans la bibliothèque standard). Les détails d'utilisation de _std::thread_ ne nous concernent pas ici; assurez-vous simplement de joindre ou de détacher thr éventuellement. Ce qui compte, c'est que chaque fois que l'appel de fonction se termine, notre résultat est prêt:

    _auto res = fut.get();  // as before
    _
  3. Nous en sommes maintenant au niveau le plus bas: comment pourrions-nous mettre en œuvre la tâche empaquetée? C'est ici que std::promise entre en jeu. La promesse est la pierre angulaire de la communication avec le futur. Les principales étapes sont les suivantes:

    • Le fil d'appel fait une promesse.

    • Le fil d'appel obtient un avenir de la promesse.

    • La promesse, ainsi que les arguments de la fonction, sont déplacés dans un thread séparé.

    • Le nouveau thread exécute la fonction et remplit la promesse.

    • Le fil d'origine récupère le résultat.

    À titre d'exemple, voici notre propre "tâche empaquetée":

    _template <typename> class my_task;
    
    template <typename R, typename ...Args>
    class my_task<R(Args...)>
    {
        std::function<R(Args...)> fn;
        std::promise<R> pr;             // the promise of the result
    public:
        template <typename ...Ts>
        explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { }
    
        template <typename ...Ts>
        void operator()(Ts &&... ts)
        {
            pr.set_value(fn(std::forward<Ts>(ts)...));  // fulfill the promise
        }
    
        std::future<R> get_future() { return pr.get_future(); }
    
        // disable copy, default move
    };
    _

    L'utilisation de ce modèle est essentiellement la même que celle de _std::packaged_task_. Notez que déplacer la tâche entière suppose de déplacer la promesse. Dans des situations plus ponctuelles, vous pouvez également déplacer explicitement un objet de promesse dans le nouveau thread et en faire un argument de fonction de la fonction de thread, mais un wrapper de tâche comme celui ci-dessus apparaît comme une solution plus souple et moins intrusive.


Faire des exceptions

Les promesses sont intimement liées aux exceptions. L'interface d'une promesse à elle seule ne suffit pas pour exprimer complètement son état, aussi des exceptions sont-elles générées chaque fois qu'une opération sur une promesse n'a pas de sens. Toutes les exceptions sont de type _std::future_error_, qui dérive de _std::logic_error_. Tout d'abord, une description de certaines contraintes:

  • Une promesse construite par défaut est inactive. Les promesses inactives peuvent mourir sans conséquence.

  • Une promesse devient active lorsqu'un futur est obtenu via get_future(). Cependant, seul n futur peut être obtenu!

  • Une promesse doit soit être satisfaite via set_value(), soit faire l'objet d'une exception via set_exception() avant la fin de sa durée de vie, si son avenir doit être consommé. Une promesse satisfaite peut mourir sans conséquence et get() devient disponible pour le futur. Une promesse avec une exception lèvera l'exception stockée à l'appel de get() dans le futur. Si la promesse décède sans valeur ni exception, appeler get() à l'avenir déclenchera une exception "promesse non tenue".

Voici une petite série de tests pour démontrer ces différents comportements exceptionnels. Tout d'abord, le harnais:

_#include <iostream>
#include <future>
#include <exception>
#include <stdexcept>

int test();

int main()
{
    try
    {
        return test();
    }
    catch (std::future_error const & e)
    {
        std::cout << "Future error: " << e.what() << " / " << e.code() << std::endl;
    }
    catch (std::exception const & e)
    {
        std::cout << "Standard exception: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "Unknown exception." << std::endl;
    }
}
_

Passons maintenant aux tests.

Cas 1: promesse inactive

_int test()
{
    std::promise<int> pr;
    return 0;
}
// fine, no problems
_

Cas 2: Promesse active, non utilisée

_int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();
    return 0;
}
// fine, no problems; fut.get() would block indefinitely
_

Cas 3: trop de futures

_int test()
{
    std::promise<int> pr;
    auto fut1 = pr.get_future();
    auto fut2 = pr.get_future();  //   Error: "Future already retrieved"
    return 0;
}
_

Cas 4: promesse satisfaite

_int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
    }

    return fut.get();
}
// Fine, returns "10".
_

Cas 5: trop de satisfaction

_int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
        pr2.set_value(10);  // Error: "Promise already satisfied"
    }

    return fut.get();
}
_

La même exception est levée s'il existe plus d'un des soit ​​de _set_value_ ou _set_exception_.

Cas 6: Exception

_int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_exception(std::make_exception_ptr(std::runtime_error("Booboo")));
    }

    return fut.get();
}
// throws the runtime_error exception
_

Cas 7: promesse non tenue

_int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
    }   // Error: "broken promise"

    return fut.get();
}
_
468
Kerrek SB

Bartosz Milewski fournit une bonne rédaction.

C++ divise l'implémentation des futures en un ensemble de petits blocs

std :: promise est l'une de ces parties.

Une promesse est un moyen de transmettre la valeur de retour (ou une exception) du thread qui exécute une fonction au thread qui récupère la fonction future.

...

Un futur est l'objet de synchronisation construit autour de l'extrémité de réception du canal de promesse.

Donc, si vous voulez utiliser un avenir, vous vous retrouvez avec une promesse que vous utilisez pour obtenir le résultat du traitement asynchrone.

Un exemple de la page est:

promise<int> intPromise;
future<int> intFuture = intPromise.get_future();
std::thread t(asyncFun, std::move(intPromise));
// do some other stuff
int result = intFuture.get(); // may throw MyException
32
Paul Rubel

En gros, vous pouvez considérer std::promise comme l'autre extrémité d'un std::future (c'est false, mais à titre d'illustration, vous pouvez penser comme si c'était le cas). L'extrémité consommateur du canal de communication utiliserait un std::future pour consommer la donnée de l'état partagé, tandis que le processus producteur utiliserait un std::promise pour écrire dans l'état partagé.

std::promise est le canal ou le chemin d'accès aux informations à renvoyer à partir de la fonction asynchrone. std::future est le mécanisme de synchronisation qui fait attendre l'appelant jusqu'à ce que la valeur renvoyée dans le std::promise soit prête (ce qui signifie que sa valeur est définie dans la fonction).

10
kjp

Il existe en réalité 3 entités principales dans le traitement asynchrone. C++ 11 se concentre actuellement sur 2 d'entre eux.

Les éléments essentiels dont vous avez besoin pour exécuter une logique de manière asynchrone sont les suivants:

  1. La tâche (logique conditionnée comme un objet foncteur) qui s'exécutera "quelque part".
  2. Le nœud de traitement actuel - un thread, un processus, etc., qui exécute de tels foncteurs quand ils lui sont fournis. Consultez le modèle de conception "Commande" pour avoir une bonne idée de la manière dont un pool de threads de travail de base effectue cela.
  3. Le résultat: Quelqu'un a besoin de ce résultat et a besoin d'un objet qui l'obtiendra pour lui. Pour OOP et pour d'autres raisons, toute attente ou synchronisation doit être effectuée dans les API de ce descripteur.

C++ 11 appelle les choses dont je parle dans (1) std::promise et celles dans (3) std::future. std::thread est la seule chose fournie publiquement pour (2). Ceci est regrettable car les programmes réels doivent gérer les ressources de threads et de mémoire, et la plupart voudront que les tâches s’exécutent sur des pools de threads au lieu de créer et de détruire un thread pour chaque petite tâche (ce qui entraîne presque toujours des performances inutiles et permet de créer facilement des ressources. la famine est encore pire).

Selon Herb Sutter et d’autres personnes du groupe C++ 11, il est provisoirement envisagé d’ajouter un std::executor qui, tout comme en Java, constituera la base des pools de threads et des configurations logiquement similaires pour (2). Peut-être que nous verrons cela en C++ 2014, mais mon pari est plus proche de C++ 17 (et que Dieu nous vienne en aide s'ils basent la norme en la matière).

7
Zack Yezek

Un std::promise est créé en tant que point final d'une paire promesse/future et le std::future (créé à partir de std :: promise à l'aide de la méthode get_future()) est l'autre extrémité. Il s'agit d'une méthode simple et unique permettant aux deux threads de se synchroniser, un thread fournissant les données à un autre thread par le biais d'un message.

Vous pouvez le voir comme un fil crée une promesse de fournir des données et l'autre fil recueille la promesse dans le futur. Ce mécanisme ne peut être utilisé qu'une seule fois.

Le mécanisme promesse/avenir n’est qu’un sens, du thread qui utilise la méthode set_value() d’un std::promise au thread qui utilise la get() d’un std::future pour recevoir les données. Une exception est générée si la méthode get() d'un futur est appelée plusieurs fois.

Si le thread avec le std::promise n'a pas utilisé set_value() pour remplir sa promesse, lorsque le second thread appelle get() du std::future pour collecter la promesse, le second thread passera dans un état d'attente jusqu'à ce que la promesse soit rempli par le premier thread avec le std::promise quand il utilise la méthode set_value() pour envoyer les données.

Avec les coroutines proposées de Spécification technique N4663 - Langages de programmation - Extensions C++ pour les Coroutines et le support du compilateur Visual Studio 2017 C++ de co_await, il est également possible d'utiliser std::future et std::async pour écrire la fonctionnalité de coroutine. Voir la discussion et l'exemple dans https://stackoverflow.com/a/50753040/146697 qui contient une section décrivant l'utilisation de std::future avec co_await.

L'exemple de code suivant, une simple application de console Windows Visual Studio 2013, illustre l'utilisation de quelques classes/modèles de concurrence C++ 11 et d'autres fonctionnalités. Il illustre une utilisation qui fonctionne bien pour promesse/avenir, des threads autonomes qui effectuent certaines tâches et s’arrêtent, et une utilisation dans laquelle un comportement plus synchrone est requis et en raison de la nécessité de notifications multiples, la paire promesse/avenir ne fonctionne pas.

Une note à propos de cet exemple concerne les retards ajoutés à divers endroits. Ces retards ont été ajoutés uniquement pour que les différents messages imprimés sur la console à l'aide de std::cout soient clairs et que le texte des différents threads ne soit pas mélangé.

La première partie de main() crée trois threads supplémentaires et utilise std::promise et std::future pour envoyer des données entre les threads. Un point intéressant est le moment où le fil principal démarre un fil, T2, qui attend les données du fil principal, fait quelque chose, puis envoie des données au troisième fil, T3, qui fait ensuite quelque chose et renvoie les données au fil principal.

La deuxième partie de la main() crée deux threads et un ensemble de files d'attente pour autoriser plusieurs messages du thread principal à chacun des deux threads créés. Nous ne pouvons pas utiliser std::promise et std::future pour cela car le duo promesse/avenir ne fait qu'un, et ne peut pas être utilisé à plusieurs reprises.

Le code source de la classe Sync_queue provient du langage de programmation C++: 4ème édition de Stroustrup.

// cpp_threads.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <thread>  // std::thread is defined here
#include <future>  // std::future and std::promise defined here

#include <list>    // std::list which we use to build a message queue on.

static std::atomic<int> kount(1);       // this variable is used to provide an identifier for each thread started.

//------------------------------------------------
// create a simple queue to let us send notifications to some of our threads.
// a future and promise are one shot type of notifications.
// we use Sync_queue<> to have a queue between a producer thread and a consumer thread.
// this code taken from chapter 42 section 42.3.4
//   The C++ Programming Language, 4th Edition by Bjarne Stroustrup
//   copyright 2014 by Pearson Education, Inc.
template<typename Ttype>
class Sync_queue {
public:
    void  put(const Ttype &val);
    void  get(Ttype &val);

private:
    std::mutex mtx;                   // mutex used to synchronize queue access
    std::condition_variable cond;     // used for notifications when things are added to queue
    std::list <Ttype> q;              // list that is used as a message queue
};

template<typename Ttype>
void Sync_queue<Ttype>::put(const Ttype &val) {
    std::lock_guard <std::mutex> lck(mtx);
    q.Push_back(val);
    cond.notify_one();
}

template<typename Ttype>
void Sync_queue<Ttype>::get(Ttype &val) {
    std::unique_lock<std::mutex> lck(mtx);
    cond.wait(lck, [this]{return  !q.empty(); });
    val = q.front();
    q.pop_front();
}
//------------------------------------------------


// thread function that starts up and gets its identifier and then
// waits for a promise to be filled by some other thread.
void func(std::promise<int> &jj) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();   // wait for the promise attached to the future
    std::cout << "  func " << myId << " future " << ll << std::endl;
}

// function takes a promise from one thread and creates a value to provide as a promise to another thread.
void func2(std::promise<int> &jj, std::promise<int>&pp) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();     // wait for the promise attached to the future

    auto promiseValue = ll * 100;   // create the value to provide as promised to the next thread in the chain
    pp.set_value(promiseValue);
    std::cout << "  func2 " << myId << " promised " << promiseValue << " ll was " << ll << std::endl;
}

// thread function that starts up and waits for a series of notifications for work to do.
void func3(Sync_queue<int> &q, int iBegin, int iEnd, int *pInts) {
    int myId = std::atomic_fetch_add(&kount, 1);

    int ll;
    q.get(ll);    // wait on a notification and when we get it, processes it.
    while (ll > 0) {
        std::cout << "  func3 " << myId << " start loop base " << ll << " " << iBegin << " to " << iEnd << std::endl;
        for (int i = iBegin; i < iEnd; i++) {
            pInts[i] = ll + i;
        }
        q.get(ll);  // we finished this job so now wait for the next one.
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::chrono::milliseconds myDur(1000);

    // create our various promise and future objects which we are going to use to synchronise our threads
    // create our three threads which are going to do some simple things.
    std::cout << "MAIN #1 - create our threads." << std::endl;

    // thread T1 is going to wait on a promised int
    std::promise<int> intPromiseT1;
    std::thread t1(func, std::ref(intPromiseT1));

    // thread T2 is going to wait on a promised int and then provide a promised int to thread T3
    std::promise<int> intPromiseT2;
    std::promise<int> intPromiseT3;

    std::thread t2(func2, std::ref(intPromiseT2), std::ref(intPromiseT3));

    // thread T3 is going to wait on a promised int and then provide a promised int to thread Main
    std::promise<int> intPromiseMain;
    std::thread t3(func2, std::ref(intPromiseT3), std::ref(intPromiseMain));

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2 - provide the value for promise #1" << std::endl;
    intPromiseT1.set_value(22);

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.2 - provide the value for promise #2" << std::endl;
    std::this_thread::sleep_for(myDur);
    intPromiseT2.set_value(1001);
    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.4 - set_value 1001 completed." << std::endl;

    std::future<int> intFutureMain(intPromiseMain.get_future());
    auto t3Promised = intFutureMain.get();
    std::cout << "MAIN #2.3 - intFutureMain.get() from T3. " << t3Promised << std::endl;

    t1.join();
    t2.join();
    t3.join();

    int iArray[100];

    Sync_queue<int> q1;    // notification queue for messages to thread t11
    Sync_queue<int> q2;    // notification queue for messages to thread t12

    std::thread t11(func3, std::ref(q1), 0, 5, iArray);     // start thread t11 with its queue and section of the array
    std::this_thread::sleep_for(myDur);
    std::thread t12(func3, std::ref(q2), 10, 15, iArray);   // start thread t12 with its queue and section of the array
    std::this_thread::sleep_for(myDur);

    // send a series of jobs to our threads by sending notification to each thread's queue.
    for (int i = 0; i < 5; i++) {
        std::cout << "MAIN #11 Loop to do array " << i << std::endl;
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q1.put(i + 100);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q2.put(i + 1000);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
    }

    // close down the job threads so that we can quit.
    q1.put(-1);    // indicate we are done with agreed upon out of range data value
    q2.put(-1);    // indicate we are done with agreed upon out of range data value

    t11.join();
    t12.join();
    return 0;
}

Cette application simple crée la sortie suivante.

MAIN #1 - create our threads.
MAIN #2 - provide the value for promise #1
  func 1 future 22
MAIN #2.2 - provide the value for promise #2
  func2 2 promised 100100 ll was 1001
  func2 3 promised 10010000 ll was 100100
MAIN #2.4 - set_value 1001 completed.
MAIN #2.3 - intFutureMain.get() from T3. 10010000
MAIN #11 Loop to do array 0
  func3 4 start loop base 100 0 to 5
  func3 5 start loop base 1000 10 to 15
MAIN #11 Loop to do array 1
  func3 4 start loop base 101 0 to 5
  func3 5 start loop base 1001 10 to 15
MAIN #11 Loop to do array 2
  func3 4 start loop base 102 0 to 5
  func3 5 start loop base 1002 10 to 15
MAIN #11 Loop to do array 3
  func3 4 start loop base 103 0 to 5
  func3 5 start loop base 1003 10 to 15
MAIN #11 Loop to do array 4
  func3 4 start loop base 104 0 to 5
  func3 5 start loop base 1004 10 to 15
6
Richard Chambers

La promesse est à l'autre bout du fil.

Imaginez que vous ayez besoin de récupérer la valeur d'un future calculé par un async. Cependant, vous ne voulez pas qu'il soit calculé dans le même fil, et vous ne créez même pas un fil "maintenant" - votre logiciel a peut-être été conçu pour choisir un fil dans un pool, de sorte que vous ne le sachiez pas qui effectuera le calcul à la fin.

Maintenant, que passez-vous à ce thread/classe/entité (encore inconnu)? Vous ne transmettez pas la future, puisqu'il s'agit du résultat . Vous voulez passer quelque chose qui est connecté au future et qui représente l'autre extrémité du fil, vous allez donc simplement interroger la future sans savoir qui va réellement calculer/écrire quelque chose.

C'est le promise. C'est un handle connecté à votre future. Si le future est un haut-parleur, et avec get() vous commencez à écouter jusqu'à ce que du son soit émis, le promise est un microphone ; mais pas n'importe quel microphone, c’est le microphone connecté par un seul fil au haut-parleur que vous tenez. Vous savez peut-être qui se trouve à l'autre bout du fil, mais vous n'avez pas besoin de le savoir, il vous suffit de le donner et d'attendre que l'autre partie dise quelque chose.

1
Narcolessico

http://www.cplusplus.com/reference/future/promise/

Une explication de phrase: furture :: get () attend promse :: set_value () pour toujours.

void print_int(std::future<int>& fut) {
    int x = fut.get(); // future would wait prom.set_value forever
    std::cout << "value: " << x << '\n';
}

int main()
{
    std::promise<int> prom;                      // create promise

    std::future<int> fut = prom.get_future();    // engagement with future

    std::thread th1(print_int, std::ref(fut));  // send future to new thread

    prom.set_value(10);                         // fulfill promise
                                                 // (synchronizes with getting the future)
    th1.join();
    return 0;
}
0
Zhang