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?
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 implementacjistd::async
(dlatego może zrobić więcej niżstd::async
, jeśli jest używana razem z innymi niższymi rzeczami, takimi jakstd::thread
). Po prostu wypowiedziane astd::packaged_task
jeststd::function
połączonym zstd::future
istd::async
zawija i wywołuje astd::packaged_task
(być może w innym wątku).
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ść .
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."
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