Wanneer moet ik std::promise
gebruiken over std::async
of std::packaged_task
?
Kun je me praktische voorbeelden geven van wanneer je ze allemaal moet gebruiken?
Antwoord 1, autoriteit 100%
std::async
std::async
is een nette en gemakkelijke manier om een std::future
te krijgen , maar:
-
Er wordt niet altijd een nieuwe thread gestart; de enum-waarde
std::launch::async
kan als eerste argument worden doorgegeven aanstd::async
om ervoor te zorgen dat er een nieuwe thread wordt gemaakt om de taak uit te voeren gespecificeerd doorfunc
, waardoor wordt gegarandeerd datfunc
asynchroon wordt uitgevoerd.auto f = std::async( std::launch::async, func );
-
De vernietiger van
std::future
kanblokkeren totdat de nieuwe thread is voltooid.auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); }; { auto f = std::async( std::launch::async, sleep, 5 ); }
Normaal verwachten we dat alleen .get()
of .wait()
blokken, maar voor een std::future
geretourneerd van std::async
, de destructor kan ook blokkeren, dus pas op dat je je hoofdthread niet blokkeert door hem te vergeten.
-
Als de
std::future
is opgeslagen in een tijdelijk object, wordt de aanroepstd::async
geblokkeerd op het punt van vernietiging van het object, dus het volgende blok duurt 10 secondenals je deauto f =
initialisaties verwijdert. Het blokkeert slechts 5 secondenanders, omdat de twee slaapstanden gelijktijdig zullen zijn, met een wachttijd voor beide om te voltooien als gevolg van de vernietiging van de twee objecten aan het einde van het blok:auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); }; { auto f1 = std::async( std::launch::async, sleep, 5 ); auto f2 = std::async( std::launch::async, sleep, 5 ); }
std::packaged_task
std::packaged_task
heeft op zichzelf niets met threads te maken: het is gewoon een functor en een gerelateerde std::future
. Overweeg het volgende:
auto task = [](int i) {
std::this_thread::sleep_for(std::chrono::seconds(5));
return i+100;
};
std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
package(1);
std::cout << f.get() << "\n";
Hier voeren we de taak gewoon uit met package(1)
, en nadat het terugkeert, is f
klaar, dus geen blokkering op .get()
.
Er is een functie van std::packaged_task
die het erg handig maakt voor discussielijnen. In plaats van alleen een functie, kun je std::thread
initialiseren met een std::packaged_task
wat een erg leuke manier is om naar de ‘std::future’ te gaan. Overweeg het volgende:
std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
std::thread t { std::move(package), 5 };
std::cout << f.get() << "\n"; //block here until t finishes
t.join();
Omdat std::packaged_task
niet kopieerbaar is, moet je het naar een nieuwe thread verplaatsen met std::move
.
std::promise
std::promise
is een krachtige mechanisme. U kunt bijvoorbeeld een waarde doorgeven aan een nieuwe thread zonder dat er extra synchronisatie nodig is.
auto task = [](std::future<int> i) {
std::cout << i.get() << std::flush;
};
std::promise<int> p;
std::thread t{ task, p.get_future() };
std::this_thread::sleep_for(std::chrono::seconds(5));
p.set_value(5);
t.join();
Nieuwe thread wacht op ons op .get()
Dus, in het algemeen, uw vraag beantwoorden:
-
Gebruik
std::async
alleen voor simpele dingen, bijv. om sommige oproepen niet-blokkerend te maken, maar houd rekening met de opmerkingen over blokkeren hierboven. -
Gebruik
std::packaged_task
om gemakkelijk naar eenstd::future
te gaan en voer het uit als een aparte threadstd::thread{ std::move(package), param }.detach();
of
std::thread t { std::move(package), param };
- Gebruik
std::promise
wanneer je meer controle over de toekomst nodig hebt.
Zie ook std::shared_future
en over het doorgeven van uitzonderingen tussen threads std::promise::set_exception
Antwoord 2
Een belofte wordt gebruikt om een waarde op te slaan die is berekend met b.v. een std::async.
Zie http://en.cppreference.com/w/cpp/thread/promise
Ik kan me voorstellen dat je je afvraagt wat het verschil is tussen std::packaged_task en std::async (in de meest gebruikelijke benadering start std::async NU een aparte thread om function/lambda/etc uit te voeren met een (waarschijnlijk) dure berekening.
Een std::packaged_task wordt gebruikt om een functie/lambda/etc te verpakken met de huidige waarden van argumenten, zodat u deze LATER kunt uitvoeren, synchroon of in een aparte thread).
Zowel std::packaged_task als std::async bieden een std::future die het RESULTAAT zal bevatten van de ingepakte functie/lambda/etc zodra deze is uitgevoerd.
Intern gebruikt de std::future een std::promise om dat resultaat vast te houden.