Jaka jest różnica między pakietowanym zadaniem a asynchronizacją

Podczas pracy z modelem gwintowanym C++11 zauważyłem, że

std::packaged_task<int(int,int)> task([](int a, int b) { return a + b; });
auto f = task.get_future();
task(2,3);
std::cout << f.get() << '\n';

I

auto f = std::async(std::launch::async, 
    [](int a, int b) { return a + b; }, 2, 3);
std::cout << f.get() << '\n';
Wygląda na to, że robią dokładnie to samo. Rozumiem, że może być duża różnica, gdybym biegł std::async z std::launch::deferred, ale czy jest taka w tym przypadku?

Jaka jest różnica między tymi dwoma podejściami, a co ważniejsze, w jakich przypadkach użyć jednego nad drugim?

Author: chema989, 2013-08-09

3 answers

W rzeczywistości przykład, który podałeś, pokazuje różnice, jeśli używasz dość długiej funkcji, takiej jak

//! sleeps for one second and returns 1
auto sleep = [](){
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 1;
};

Zadanie spakowane

A packaged_task nie uruchomi się samodzielnie, musisz to wywołać:

std::packaged_task<int()> task(sleep);

auto f = task.get_future();
task(); // invoke the function

// You have to wait until task returns. Since task calls sleep
// you will have to wait at least 1 second.
std::cout << "You can see this after 1 second\n";

// However, f.get() will be available, since task has already finished.
std::cout << f.get() << std::endl;

std::async

Z drugiej strony, std::async z launch::async spróbuje uruchomić zadanie w innym wątku:

auto f = std::async(std::launch::async, sleep);
std::cout << "You can see this immediately!\n";

// However, the value of the future will be available after sleep has finished
// so f.get() can block up to 1 second.
std::cout << f.get() << "This will be shown after a second!\n";

Zwrot

Ale zanim spróbujesz użyć async do wszystkiego, pamiętaj, że zwrócona przyszłość ma specjalny wspólny stan, który future::~future blokuje:

std::async(do_work1); // ~future blocks
std::async(do_work2); // ~future blocks

/* output: (assuming that do_work* log their progress)
    do_work1() started;
    do_work1() stopped;
    do_work2() started;
    do_work2() stopped;
*/

Więc jeśli chcesz prawdziwego asynchronicznego, musisz zachować zwrócony future, lub jeśli nie zależy ci na wyniku, jeśli okoliczności się zmienią:

{
    auto pizza = std::async(get_pizza);
    /* ... */
    if(need_to_go)
        return;          // ~future will block
    else
       eat(pizza.get());
}   

Więcej informacji na ten temat można znaleźć w artykule Herb Sutter async oraz ~future, który opisuje problem, a Scott Meyer std::futures z std::async nie są specjalne , które opisuje spostrzeżenia. Należy również pamiętać, że to zachowanie zostało określone w C++14 i nowszych, ale także powszechnie zaimplementowane w C++11.

Dalsze różnice

Używając std::async nie można już uruchomić zadania w określonym wątku, gdzie std::packaged_task można przenieść do innych wątków.

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::thread myThread(std::move(task),2,3);

std::cout << f.get() << "\n";
W przeciwnym razie program zostanie zamrożony, ponieważ przyszłość nigdy nie będzie gotowa:]}
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);

TL;DR

Użyj std::async jeśli chcesz, aby niektóre rzeczy zostały zrobione i nie obchodzi cię, kiedy są zrobione, i std::packaged_task jeśli chcesz zakończyć rzeczy, aby przenieść je do innych wątków lub wywołać je później. Czy, cytując Christian :

W końcu std::packaged_task jest tylko funkcją niższego poziomu do implementacji std::async (dlatego może zrobić więcej niż std::async, jeśli jest używana razem z innymi niższymi rzeczami, takimi jak std::thread). Po prostu wypowiedziane a std::packaged_task jest std::function połączonym z std::future i std::async zawija i wywołuje a std::packaged_task (być może w innym wątku).

 167
Author: Zeta,
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
2018-05-21 10:17:12

Packaged Task vs async

p> Packaged task zawiera zadanie [function or function object] oraz parę future / promise. Gdy zadanie wykonuje polecenie return, powoduje set_value(..) na packaged_task's promise.

a> biorąc pod uwagę przyszłość, obietnicę i zadania pakietowe, możemy tworzyć proste zadania bez martwienia się zbytnio o wątki [thread jest tylko czymś, co dajemy do uruchomienia zadania].

Musimy jednak rozważyć ile wątków użyć lub czy zadanie najlepiej uruchomić na aktualny wątek lub na innym itp.Takie descendencje mogą być obsługiwane przez program uruchamiający wątek o nazwie async(), który decyduje, czy utworzyć nowy wątek a, czy poddać recyklingowi stary lub po prostu uruchomić zadanie na bieżącym wątku. Zwraca przyszłość .

 1
Author: maxshuty,
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
2015-10-20 14:53:39

" szablon klasy std:: packaged_task zawija dowolny obiekt wywołujący (function, lambda expression, bind expression, or another function obiekt) tak, że można go wywoływać asynchronicznie. Jego wartość zwracana lub wyjątek wyrzucony jest przechowywany w stanie współdzielonym, do którego można uzyskać dostęp poprzez std:: future objects."

" funkcja szablonu asynchronicznie uruchamia funkcję f (potencjalnie w osobnym wątku) i zwraca std:: future, który będzie / align = "left" / wynik wywołania tej funkcji."

 0
Author: Radoslav.B,
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
2013-08-09 09:44:44