Wanneer gebruik ik belofte over asynchrone of verpakte_taak?

Wanneer moet ik std::promisegebruiken over std::asyncof std::packaged_task?
Kun je me praktische voorbeelden geven van wanneer je ze allemaal moet gebruiken?


Antwoord 1, autoriteit 100%

std::async

std::asyncis een nette en gemakkelijke manier om een ​​std::futurete krijgen , maar:

  • Er wordt niet altijd een nieuwe thread gestart; de enum-waarde std::launch::asynckan als eerste argument worden doorgegeven aan std::asyncom ervoor te zorgen dat er een nieuwe thread wordt gemaakt om de taak uit te voeren gespecificeerd door func, waardoor wordt gegarandeerd dat funcasynchroon wordt uitgevoerd.

     auto f = std::async( std::launch::async, func );
    
  • De vernietiger van std::futurekanblokkeren 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::futuregeretourneerd van std::async, de destructor kan ook blokkeren, dus pas op dat je je hoofdthread niet blokkeert door hem te vergeten.

  • Als de std::futureis opgeslagen in een tijdelijk object, wordt de aanroep std::asyncgeblokkeerd op het punt van vernietiging van het object, dus het volgende blok duurt 10 secondenals je de auto 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_taskheeft 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 fklaar, dus geen blokkering op .get().

Er is een functie van std::packaged_taskdie het erg handig maakt voor discussielijnen. In plaats van alleen een functie, kun je std::threadinitialiseren met een std::packaged_taskwat 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_taskniet kopieerbaar is, moet je het naar een nieuwe thread verplaatsen met std::move.

std::promise

std::promiseis 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::asyncalleen voor simpele dingen, bijv. om sommige oproepen niet-blokkerend te maken, maar houd rekening met de opmerkingen over blokkeren hierboven.

  • Gebruik std::packaged_taskom gemakkelijk naar een std::futurete gaan en voer het uit als een aparte thread

     std::thread{ std::move(package), param }.detach();
    

of

   std::thread t { std::move(package), param };
  • Gebruik std::promisewanneer je meer controle over de toekomst nodig hebt.

Zie ook std::shared_futureen 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.

Other episodes