Co to jest std:: promise?

Jestem dość zaznajomiony z C++11 ' S std::thread, std::async i std::future składniki (np. patrz ta ODPOWIEDŹ ), które są proste.

Nie mogę jednak do końca zrozumieć, czym jest std::promise, co robi i w jakich sytuacjach najlepiej go używać. Sam standardowy dokument nie zawiera wielu informacji poza jego klasą synopsis, podobnie jak po prostu:: thread .

Czy ktoś mógłby podać krótki, zwięzły przykład sytuacji, w której potrzebny jest std::promise i gdzie jest najbardziej idiomatyczne rozwiązanie?

Author: cybermonkey, 2012-06-13

8 answers

W słowach [futures.stan] a std::future jest asynchronicznym obiektem return ("obiekt, który odczytuje wyniki ze stanu współdzielonego") i a std::promise jest asynchronicznym dostawcą ("obiekt, który dostarcza wynik do stanu współdzielonego") tzn. obietnica jest rzeczą, którą ustawiasz wynik, aby uzyskać to z powiązanej przyszłości.

Asynchroniczny dostawca jest tym, co początkowo tworzy wspólny stan, do którego odnosi się przyszłość. std::promise jest jeden typ dostawcy asynchronicznego, std::packaged_task Jest Inny, a wewnętrzny detal {[4] } jest inny. Każdy z nich może utworzyć stan współdzielony i dać std::future, który dzieli ten stan i może go przygotować.

std::async jest wygodnym narzędziem wyższego poziomu, które zapewnia asynchroniczny obiekt wynikowy i wewnętrznie zajmuje się tworzeniem asynchronicznego dostawcy i przygotowaniem współdzielonego stanu po zakończeniu zadania. Można go emulować za pomocą std::packaged_task (lub std::bind i std::promise) i std::thread, ale jest bezpieczniejszy i łatwiejszy w użyciu std::async.

std::promise jest nieco niższym poziomem, ponieważ gdy chcesz przekazać asynchroniczny wynik do przyszłości, ale kod, który sprawia, że wynik jest gotowy, nie może być zawinięty w jedną funkcję odpowiednią do przekazania do std::async. Na przykład, możesz mieć tablicę kilku promises i powiązanych future S i mieć pojedynczy wątek, który wykonuje kilka obliczeń i ustawia wynik dla każdej obietnicy. async umożliwi zwrócenie tylko jednego wyniku, aby zwrócić kilka, musisz zadzwonić async kilka razy, co może zmarnować zasoby.

 155
Author: Jonathan Wakely,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-03 10:19:45

Rozumiem teraz nieco lepiej sytuację (w niedużej ilości ze względu na odpowiedzi tutaj!), więc pomyślałem, że dodam trochę własnego zapisu.


W C++11 istnieją dwa różne, choć powiązane pojęcia: asynchroniczne obliczenia( funkcja, która jest wywoływana gdzie indziej) i wykonywanie współbieżne (wątek {47]} {48]}, coś, co działa jednocześnie). Te dwa pojęcia są nieco ortogonalne. Obliczenia asynchroniczne to tylko inny smak wywołania funkcji, podczas gdy wątek jest kontekstem wykonania. Wątki są przydatne same w sobie, ale na potrzeby tej dyskusji potraktuję je jako szczegół implementacji.


Istnieje hierarchia abstrakcji dla obliczeń asynchronicznych. Na przykład, załóżmy, że mamy funkcję, która pobiera pewne argumenty:

int foo(double, char, bool);

Po pierwsze, mamy szablon std::future<T>, która reprezentuje przyszłą wartość typu T. Wartość można pobrać poprzez funkcja member get(), która skutecznie synchronizuje program oczekując na wynik. Alternatywnie, future obsługuje wait_for(), który może być użyty do sprawdzenia, czy wynik jest już dostępny. Kontrakty Futures należy traktować jako asynchroniczny zamiennik drop-in dla zwykłych typów zwrotu. Dla przykładowej funkcji oczekujemy std::future<int>.

Teraz, do hierarchii, od najwyższego do najniższego poziomu:

  1. std::async: najwygodniejszy i prosta droga do wykonania obliczeń asynchronicznych jest za pomocą szablonu funkcji async, który natychmiast zwraca dopasowaną przyszłość:

    auto fut = std::async(foo, 1.5, 'x', false);  // is a std::future<int>
    
    Mamy bardzo małą kontrolę nad szczegółami. W szczególności, nie wiemy nawet, czy funkcja jest wykonywana równolegle, szeregowo na get(), czy też przez jakąś inną czarną magię. Jednak wynik jest łatwo uzyskany w razie potrzeby:
    auto res = fut.get();  // is an int
    
  2. Możemy teraz rozważyć, jak zaimplementować coś w rodzaju async, ale w sposób, którymy kontrolujemy. Na przykład, możemy nalegać, aby funkcja była wykonywana w osobnym wątku. Wiemy już, że możemy dostarczyć osobny wątek za pomocą std::thread klasy.

    Następny niższy poziom abstrakcji robi dokładnie to: std::packaged_task. Jest to szablon, który zawija funkcję i zapewnia przyszłość dla zwracanej wartości funkcji, ale sam obiekt można wywołać, a wywołanie go jest według uznania użytkownika. Możemy ustaw to tak:

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

    Przyszłość staje się gotowa, gdy wywołamy zadanie i połączenie się zakończy. Jest to idealne zadanie dla oddzielnego wątku. Musimy tylko upewnić się, że przeniesie zadanie do wątku:

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

    Wątek zaczyna działać natychmiast. Możemy albo detach it, albo mieć join it na końcu zakresu, lub kiedykolwiek (np. używając wrappera Anthony ' ego Williamsa scoped_thread, który naprawdę powinien być w bibliotece standardowej). Szczegóły korzystania std::thread nie martw się o nas tutaj, ale pamiętaj, aby dołączyć lub odłączyć {30]} W końcu. Ważne jest to, że gdy wywołanie funkcji zakończy się, nasz wynik jest gotowy: {]}

    auto res = fut.get();  // as before
    
  3. Teraz jesteśmy na najniższym poziomie: jak zaimplementowalibyśmy zadanie spakowane? To tutaj std::promise wchodzi. Obietnica jest podstawą komunikacji z przyszłością. Główne kroki to:

    • Wątek wywołujący sprawia, że obiecuję.

    • Wątek wywołujący uzyskuje przyszłość z obietnicy.

    • Obietnica, wraz z argumentami funkcji, są przenoszone do osobnego wątku.

    • Nowy wątek wykonuje funkcję i wypełnia obietnicę.

    • Oryginalny wątek pobiera wynik.

    Na przykład, oto nasze własne "spakowane zadanie":]}
    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
    };
    

    Użycie tego szablonu jest zasadniczo takie samo jak w przypadku std::packaged_task. Zwróć uwagę, że przeniesienie całego zadania ustępuje przesunięciu obietnicy. W bardziej doraźnych sytuacjach można również przenieść obiekt promise bezpośrednio do nowego wątku i uczynić go argumentem funkcji funkcji wątku, ale owijarka zadań, taka jak ta powyżej, wydaje się bardziej elastycznym i mniej inwazyjnym rozwiązaniem.


Tworzenie WYJĄTKÓW

Obietnice są ściśle związane z wyjątkami. Sam interfejs obietnicy nie wystarczy, aby przekazać jego stan całkowicie, więc wyjątki są wyrzucane, gdy operacja na obietnicy nie ma sensu. Wszystkie wyjątki są typu std::future_error, który wywodzi się z std::logic_error. Po pierwsze, opis niektórych ograniczeń:
  • Domyślnie skonstruowana obietnica jest nieaktywna. Nieaktywne obietnice mogą umrzeć bez konsekwencji.

  • Obietnica staje się aktywna, gdy przyszłość jest uzyskiwana przez get_future(). Jednak tylko jedna przyszłość może być uzyskana!

  • A obietnica musi być spełniona przez set_value() lub mieć wyjątek ustawiony przez set_exception() przed zakończeniem jej życia, jeśli jej przyszłość ma zostać skonsumowana. Spełniona obietnica może umrzeć bez konsekwencji, a[17] staje się dostępna w przyszłości. Obietnica z wyjątkiem wywoła zapisany wyjątek po wywołaniu get() w przyszłości. Jeśli obietnica umrze bez wartości ani wyjątku, wywołanie get() w przyszłości wywoła wyjątek "broken promise".

Oto mały test serii, aby zademonstrować te różne wyjątkowe zachowania. Najpierw uprząż:

#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;
    }
}
Przejdźmy do testów.

Przypadek 1: nieaktywna obietnica

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

Przypadek 2: aktywna obietnica, nieużywana

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

Przypadek 3: zbyt wiele kontraktów futures

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

Przypadek 4: spełniona obietnica

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".

Przypadek 5: zbyt wiele satysfakcji

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();
}

Ten sam wyjątek jest wyrzucany, jeśli jest więcej niż jeden z albo z set_value albo set_exception.

Przypadek 6: Wyjątek

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

Przypadek 7: złamana obietnica

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

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

    return fut.get();
}
 434
Author: Kerrek SB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-21 10:06:08

Bartosz Milewski

C++ dzieli implementację futures na zbiór z małych bloków

Std::promise jest jedną z tych części.

Obietnica jest nośnikiem przekazującym wartość zwracaną (lub wyjątek) od wątku wykonującego funkcję do wątku to wpływa na przyszłość funkcji.

...

Przyszłość to obiekt synchronizacji zbudowany wokół odbieranie końca kanału promise.

Więc, jeśli chcesz użyć przyszłości, kończysz z obietnicą, której używasz, aby uzyskać wynik przetwarzania asynchronicznego.

Przykład ze strony to:

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
 30
Author: Paul Rubel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 20:34:59

W przybliżonym przybliżeniu można rozważyć std::promise jako drugi koniec std::future (jest to fałsz , ale dla ilustracji można myśleć tak, jakby był). Koniec kanału komunikacyjnego używałby std::future do konsumowania danych ze stanu współdzielonego, podczas gdy wątek producenta używałby std::promise do zapisu do stanu współdzielonego.

 25
Author: David Rodríguez - dribeas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 20:30:27

std::promise jest kanałem lub ścieżką dla informacji, które mają być zwrócone z funkcji asynchronicznej. std::future jest mechanizmem synchronizacji, który sprawia, że wywołujący czeka, aż wartość zwracana w std::promise będzie gotowa(co oznacza, że jej wartość jest ustawiona wewnątrz funkcji).

 9
Author: kjp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 20:42:10

W asynchronicznym przetwarzaniu są tak naprawdę 3 podstawowe byty. C++11 obecnie skupia się na 2 z nich.

Podstawowe rzeczy, których potrzebujesz do asynchronicznie uruchomić logikę to:

  1. zadanie (logika spakowana jako jakiś obiekt functora), które będzie działać 'gdzieś'.
  2. rzeczywisty węzeł przetwarzania - wątek, proces itp. który uruchamia takie funktory, gdy są do niego dostarczone. Spójrz na wzorzec projektowy "Command", aby uzyskać dobry pomysł na to, jak robi to basic worker thread pool.
  3. uchwyt wyniku : ktoś potrzebuje tego wyniku i potrzebuje obiektu, który go za niego otrzyma. Dla OOP i innych powodów, każde oczekiwanie lub synchronizacja powinny być wykonywane w interfejsach API tego uchwytu.

C++11 nazywa rzeczy, o których mówię w (1) std::promise, a te w (3) std::future. {[2] } jest jedyną rzeczą udostępnioną publicznie dla (2). Jest to niefortunne, ponieważ prawdziwe programy muszą zarządzać zasobami wątków i pamięci, a większość będzie chciała zadania uruchamiane na pulach wątków zamiast tworzenia i niszczenia wątku dla każdego małego zadania (co prawie zawsze powoduje niepotrzebne uderzenia wydajności same w sobie i może łatwo spowodować głód zasobów, który jest jeszcze gorszy).

Według Herba Suttera i innych w C++11 brain trust, istnieją wstępne plany dodania std::executor, która-podobnie jak w Javie - będzie podstawą puli wątków i logicznie podobnych konfiguracji dla (2). Może zobaczymy to w C++2014, ale mój zakład jest bardziej jak C++17 (i niech Bóg ma nas w opiece, jeśli zepsują dla nich standard).

 6
Author: Zack Yezek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-28 08:10:42

A std::promise jest tworzony jako punkt końcowy dla pary obietnica / przyszłość, a std::future (utworzony z std:: promise przy użyciu metody get_future()) jest drugim punktem końcowym. Jest to prosta, jednokrotna metoda zapewnienia sposobu synchronizacji dwóch wątków, ponieważ jeden wątek dostarcza dane do innego wątku za pośrednictwem wiadomości.

Można myśleć o tym, jak jeden wątek tworzy obietnicę dostarczenia danych, a drugi wątek zbiera obietnicę w przyszłości. Mechanizm ten może być używany tylko raz.

Mechanizm obietnicy / przyszłości jest tylko jednym kierunkiem, od wątku, który używa metody set_value() z std::promise do wątku, który używa get() z std::future do odbioru danych. Wyjątek jest generowany, jeśli metoda get() przyszłości zostanie wywołana więcej niż raz.

Jeśli wątek z std::promise nie użył set_value(), aby spełnić swoją obietnicę, wtedy gdy drugi wątek wywoła get() z std::future, aby zebrać obietnicę, drugi wątek przejdzie w stan oczekiwania do momentu, gdy obietnica jest spełniona przez pierwszy wątek z std::promise, gdy używa metody set_value() do wysłania danych.

Poniższy przykładowy kod, prosta aplikacja konsoli Visual Studio 2013 Windows, pokazuje przy użyciu kilku klas/szablonów współbieżności C++11 i innych funkcji. Ilustruje użycie obietnicy / przyszłości, które działa dobrze, autonomiczne wątki, które wykonają pewne zadanie i zatrzymają się, oraz użycie, w którym wymagane jest bardziej synchroniczne zachowanie i ze względu na potrzebę wielu powiadomienia para obietnica / przyszłość nie działa.

Jedna uwaga na temat tego przykładu to opóźnienia dodane w różnych miejscach. Opóźnienia te zostały dodane tylko po to, aby upewnić się, że różne wiadomości wydrukowane na konsoli za pomocą std::cout będą jasne i że tekst z kilku wątków nie będzie mieszany.

Pierwsza część main() to tworzenie trzech dodatkowych wątków i używanie std::promise i std::future do przesyłania danych między wątkami. Ciekawym punktem jest miejsce, w którym głównym thread uruchamia wątek, T2, który będzie czekał na dane z głównego wątku, coś zrobił, a następnie wysłał dane do trzeciego wątku, T3, który następnie coś zrobi i wyśle dane z powrotem do głównego wątku.

Druga część main() tworzy dwa wątki i zestaw kolejek, aby umożliwić wiele wiadomości z głównego wątku do każdego z dwóch utworzonych wątków. Nie możemy do tego użyć std::promise i std::future, ponieważ Duet promise / future jest jednym strzałem i nie może być użyty wielokrotnie.

Źródło dla klasy Sync_queue pochodzi z Stroustrup ' s the C++ Programming Language: 4th Edition.

// 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;
}

Ta prosta aplikacja tworzy następujące wyjście.

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
 5
Author: Richard Chambers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-22 22:51:42

Obietnica to drugi koniec drutu.

Wyobraź sobie, że musisz odzyskać wartość future obliczoną przez async. Jednak nie chcesz, aby był on obliczany w tym samym wątku, a nawet nie tworzysz wątku "teraz" - być może twoje oprogramowanie zostało zaprojektowane tak, aby wybrać wątek z puli, więc nie wiesz Kto {16]} wykona obliczenia che w końcu.

Co PRZEKAZUJESZ do tego (jeszcze nieznanego) wątku / klasy / encji? Nie zdasz future, ponieważ jest to wynik . Chcesz przekazać coś, co jest podłączone do futurei to reprezentuje drugi koniec przewodu, więc po prostu zapytasz future bez wiedzy, kto faktycznie coś obliczy/napisze.

To jest promise. Jest to uchwyt podłączony do twojego future. Jeśli future jest głośnikiem , a z get() zaczynasz słuchać, dopóki nie wyjdzie jakiś dźwięk, promise jest mikrofonem ; ale nie byle jaki mikrofon, jest to mikrofon podłączony jednym przewodem do trzymanego głośnika. Możesz wiedzieć, kto jest na drugim końcu, ale nie musisz tego wiedzieć - po prostu dajesz i czekasz, aż druga strona coś powie.

 1
Author: Narcolessico,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-29 21:24:24