W rzeczywistości podany przykład pokazuje różnice, jeśli używasz dość długiej funkcji, takiej jak
auto sleep = [](){
std::this_thread::sleep_for(std::chrono::seconds(1));
return 1;
};
Zadanie w pakiecie
packaged_task
Nie rozpocznie się swoją własną, trzeba ją wywołać:
std::packaged_task<int()> task(sleep);
auto f = task.get_future();
task();
std::cout << "You can see this after 1 second\n";
std::cout << f.get() << std::endl;
std::async
Z drugiej strony, std::async
with 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";
std::cout << f.get() << "This will be shown after a second!\n";
Wada
Ale zanim spróbujesz użyć async
do wszystkiego, pamiętaj, że zwrócona przyszłość ma specjalny stan współdzielony, który wymaga future::~future
blokowania:
std::async(do_work1);
std::async(do_work2);
Więc jeśli chcesz mieć rzeczywistą asynchroniczność, musisz zachować zwracany wynik future
lub jeśli nie obchodzi Cię wynik, jeśli zmienią się okoliczności:
{
auto pizza = std::async(get_pizza);
if(need_to_go)
return;
else
eat(pizza.get());
}
Aby uzyskać więcej informacji na ten temat, zobacz artykuł Herba Suttera async
i~future
, który opisuje problem, oraz Scott Meyer std::futures
z std::async
nie są specjalne , który opisuje spostrzeżenia. Zwróć także uwagę, że to zachowanie zostało określone w C ++ 14 i nowszych , ale jest również powszechnie zaimplementowane w C ++ 11.
Dalsze różnice
Korzystając z niego std::async
, nie możesz już uruchamiać zadania na określonym wątku, skąd std::packaged_task
można je 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";
Ponadto packaged_task
należy wywołać a przed wywołaniem f.get()
, w przeciwnym razie program zawiesi się, 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";
task(2,3);
TL; DR
Użyj, std::async
jeśli chcesz, aby coś zostało zrobione i nie obchodzi std::packaged_task
cię, kiedy są zrobione, i jeśli chcesz podsumować rzeczy, aby przenieść je do innych wątków lub zadzwonić później. Lub cytując Christiana :
Ostatecznie a std::packaged_task
jest po prostu funkcją niższego poziomu do zaimplementowania std::async
(dlatego może zdziałać więcej niż w std::async
połączeniu z innymi elementami niższego poziomu, takimi jak std::thread
). Po prostu wymówione a std::packaged_task
jest std::function
połączeniem z a std::future
oraz std::async
zawija i wywołuje std::packaged_task
(prawdopodobnie w innym wątku).
std::packaged_task
jest po prostu funkcją niższego poziomu do zaimplementowaniastd::async
(dlatego może zdziałać więcej niż wstd::async
połączeniu z innymi elementami niższego poziomu, takimi jakstd::thread
). Po prostu wymówione astd::packaged_task
jeststd::function
połączeniem z astd::future
orazstd::async
zawija i wywołujestd::packaged_task
(prawdopodobnie w innym wątku).Pakowane zadanie vs async
p> Zadanie spakowane zawiera zadanie
[function or function object]
i parę przyszłość / obietnica. Kiedy zadanie wykonuje instrukcję return, powodujeset_value(..)
topackaged_task
obietnicę.a> Biorąc pod uwagę przyszłość, obietnicę i pakiet zadań, możemy tworzyć proste zadania, nie martwiąc się zbytnio o wątki [wątek to po prostu coś, co dajemy, aby uruchomić zadanie].
Musimy jednak rozważyć, ile wątków użyć lub czy zadanie najlepiej uruchamiać w bieżącym wątku, czy w innym itp. Takie opisy można obsłużyć za pomocą programu uruchamiającego wątki o nazwie
async()
, który decyduje, czy utworzyć nowy wątek, czy ponownie przetworzyć stary. jeden lub po prostu uruchom zadanie w bieżącym wątku. Zwraca przyszłość.źródło
źródło