Zoeken…


Invoering

Beloften en toekomsten worden gebruikt om een enkel object van de ene draad naar de andere te verplaatsen.

Een std::promise object wordt ingesteld door de thread die het resultaat genereert.

Een std::future object kan worden gebruikt om een waarde op te halen, om te testen of een waarde beschikbaar is of om de uitvoering te stoppen totdat de waarde beschikbaar is.

std :: future en std :: belofte

In het volgende voorbeeld wordt een belofte ingesteld voor gebruik door een andere thread:

    {
        auto promise = std::promise<std::string>();
        
        auto producer = std::thread([&]
        {
            promise.set_value("Hello World");
        });
        
        auto future = promise.get_future();
        
        auto consumer = std::thread([&]
        {
            std::cout << future.get();
        });
        
        producer.join();
        consumer.join();
}

Voorbeeld van uitgestelde async

Deze code implementeert een versie van std::async , maar het gedraagt zich alsof async altijd wordt aangeroepen met het deferred startbeleid. Deze functie heeft ook geen speciaal future gedrag van async ; de teruggekeerde future kan worden vernietigd zonder ooit zijn waarde te verwerven.

template<typename F>
auto async_deferred(F&& func) -> std::future<decltype(func())>
{
    using result_type = decltype(func());

    auto promise = std::promise<result_type>();
    auto future  = promise.get_future();

    std::thread(std::bind([=](std::promise<result_type>& promise)
    {
        try
        {
            promise.set_value(func()); 
            // Note: Will not work with std::promise<void>. Needs some meta-template programming which is out of scope for this example.
        }
        catch(...)
        {
            promise.set_exception(std::current_exception());
        }
    }, std::move(promise))).detach();

    return future;
}

std :: packaged_task en std :: future

std::packaged_task bundelt een functie en de bijbehorende belofte voor het retourtype:

template<typename F>
auto async_deferred(F&& func) -> std::future<decltype(func())>
{
    auto task   = std::packaged_task<decltype(func())()>(std::forward<F>(func));
    auto future = task.get_future();

    std::thread(std::move(task)).detach();

    return std::move(future);
}

De draad begint onmiddellijk te lopen. We kunnen het losmaken of aan het einde van de scope meedoen. Wanneer de functieaanroep naar std :: thread is voltooid, is het resultaat gereed.

Merk op dat dit enigszins verschilt van std::async waar de geretourneerde std::future wanneer vernietigd daadwerkelijk zal blokkeren totdat de thread is voltooid.

std :: future_error en std :: future_errc

Als niet aan de beperkingen voor std :: belofte en std :: future wordt voldaan, wordt een uitzondering van het type std :: future_error gegenereerd.

Het lid van de foutcode is van het type std :: future_errc en waarden zijn zoals hieronder, samen met enkele testgevallen:

enum class future_errc {
    broken_promise             = /* the task is no longer shared */,
    future_already_retrieved   = /* the answer was already retrieved */,
    promise_already_satisfied  = /* the answer was stored already */,
    no_state                   = /* access to a promise in non-shared state */
};

Inactieve belofte:

int test()
{
    std::promise<int> pr;
    return 0; // returns ok
}

Actieve belofte, ongebruikt:

  int test()
    {
        std::promise<int> pr;
        auto fut = pr.get_future(); //blocks indefinitely!
        return 0; 
    }

Dubbel ophalen:

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

    try{
        auto fut2 = pr.get_future();    //   second attempt to get future
        return 0;
    }
    catch(const std::future_error& e)
    {
        cout << e.what() << endl;       //   Error: "The future has already been retrieved from the promise or packaged_task."
        return -1;
    }
    return fut2.get();
}

Std instellen :: belofte waarde twee keer:

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();
    try{
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
        pr2.set_value(10);  // second attempt to set promise throws exception
    }
    catch(const std::future_error& e)
    {
        cout << e.what() << endl;       //   Error: "The state of the promise has already been set."
        return -1;
    }
    return fut.get();
}

std :: future en std :: async

In het volgende naïeve parallelle sorteervoorbeeld, wordt std::async gebruikt om meerdere parallelle merge_sort-taken te starten. std::future wordt gebruikt om op de resultaten te wachten en deze te synchroniseren:

#include <iostream>
using namespace std;


void merge(int low,int mid,int high, vector<int>&num)
{
    vector<int> copy(num.size());
    int h,i,j,k;
    h=low;
    i=low;
    j=mid+1;
    
    while((h<=mid)&&(j<=high))
    {
        if(num[h]<=num[j])
        {
            copy[i]=num[h];
            h++;
        }
        else
        {
            copy[i]=num[j];
            j++;
        }
        i++;
    }
    if(h>mid)
    {
        for(k=j;k<=high;k++)
        {
            copy[i]=num[k];
            i++;
        }
    }
    else
    {
        for(k=h;k<=mid;k++)
        {
            copy[i]=num[k];
            i++;
        }
    }
    for(k=low;k<=high;k++)
        swap(num[k],copy[k]);
}


void merge_sort(int low,int high,vector<int>& num)
{
    int mid;
    if(low<high)
    {
        mid = low + (high-low)/2;
        auto future1    =  std::async(std::launch::deferred,[&]()
                                      {
                                        merge_sort(low,mid,num);
                                      });
        auto future2    =  std::async(std::launch::deferred, [&]()
                                       {
                                          merge_sort(mid+1,high,num) ;
                                       });
        
        future1.get();
        future2.get();
        merge(low,mid,high,num);
    }
}

Opmerking: In het voorbeeld wordt std::async gestart met beleid std::launch_deferred . Dit om te voorkomen dat bij elke oproep een nieuwe thread wordt gemaakt. In ons voorbeeld zijn de aanroepen naar std::async buiten gebruik, ze synchroniseren bij de aanroepen voor std::future::get() .

std::launch_async dwingt bij elke oproep een nieuwe thread te maken.

Het standaardbeleid is std::launch::deferred| std::launch::async , wat betekent dat de implementatie het beleid bepaalt voor het maken van nieuwe threads.

Async operatie klassen

  • std :: async: voert een asynchrone bewerking uit.
  • std :: future: biedt toegang tot het resultaat van een asynchrone bewerking.
  • std :: belofte: verpakt het resultaat van een asynchrone bewerking.
  • std :: packaged_task: bundelt een functie en de bijbehorende belofte voor het retourtype.


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow