std async std future std promise std packagedtask

  • Slides: 45
Download presentation

О чём речь • std: : async, std: : future, std: : promise, std:

О чём речь • std: : async, std: : future, std: : promise, std: : packaged_task • PPL: concurrency: : task, continuations, cancellation, task composition • корутины: co_await, co_return, генераторы, детали реализации • будущее? 2

Метафора Асинхронный вариант: kettle. boil. Water. Async(); play. Videogames. For(5 min); kettle. wait. Water.

Метафора Асинхронный вариант: kettle. boil. Water. Async(); play. Videogames. For(5 min); kettle. wait. Water. To. Boil(); make. Tea. Async(); watch. You. Tube. For(2 min); drink. Tea(); 5

std: : async std: : future<void> tea = std: : async(std: : launch: :

std: : async std: : future<void> tea = std: : async(std: : launch: : async, []{ boil. Water(); make. Tea(); }); watch. People. Play. Games. On. You. Tube. For(7 min); tea. get(); drink. Tea(); 6

std: : packaged_task<void()> task{ []{ boil. Water(); make. Tea(); } }; std: : future<void>

std: : packaged_task<void()> task{ []{ boil. Water(); make. Tea(); } }; std: : future<void> tea = task. get_future(); help. execute(std: : move(task)); tea. get(); drink. Tea(); 7

std: : promise<int> promise; std: : future<int> tea = promise. get_future(); auto task =

std: : promise<int> promise; std: : future<int> tea = promise. get_future(); auto task = [p = std: : move(promise)]() mutable { try { std: : promise boil. Water(); shared state make. Tea(); p. set_value(42); std: : future } catch (. . . ) { p. set_exception(std: : current_exception()); } }; help. execute(std: : move(task)); tea. get(); 8

Идиома: асинхронное обновление значения struct Widget { std: : future<void> update. Value(); std: :

Идиома: асинхронное обновление значения struct Widget { std: : future<void> update. Value(); std: : string get. Value() const; private: struct State { std: : mutex; std: : string value; }; std: : shared_ptr<State> m_state = std: : make_shared<State>(); }; std: : future<void> Widget: : update. Value() { return std: : async( [state. Ptr = std: : weak_ptr{m_state}] { auto new. Value = get. Updated. Value(); if (auto state = state. Ptr. lock()) { std: : lock_guard lock(state->mutex); state->value = std: : move(new. Value); } }); } std: : string Widget: : get. Value() const { std: : lock_guard lock(m_state->mutex); return m_state->value; } 9

Parallel Patterns Library • выпущена Microsoft вместе с Visual Studio 2010 (+ лямбды) •

Parallel Patterns Library • выпущена Microsoft вместе с Visual Studio 2010 (+ лямбды) • подмножество (PPLX) реализовано в кроссплатформенной библиотеке C++ REST SDK https: //github. com/Microsoft/cpprestsdk 10

PPL concurrency: : task 101 #include <ppltasks. h> #include <future> using namespace concurrency; task<T>

PPL concurrency: : task 101 #include <ppltasks. h> #include <future> using namespace concurrency; task<T> std: : future<T> auto t = task<T>{f}; auto t = create_task(f); auto t = std: : async( std: : launch: : async, f); task_completion_event<T> std: : promise<T> 11

Task unwrapping std: : future<void> make. Tea. Async(); std: : future<void>> tea = std:

Task unwrapping std: : future<void> make. Tea. Async(); std: : future<void>> tea = std: : async([]() -> std: : future<void> { boil. Water(); return make. Tea. Async(); } ); tea. get(); drink. Tea(); 12

Task unwrapping task<void> make. Tea. Async(); auto tea = task<void>{[]() -> task<void> { boil.

Task unwrapping task<void> make. Tea. Async(); auto tea = task<void>{[]() -> task<void> { boil. Water(); return make. Tea. Async(); } }; tea. wait(); drink. Tea(); 13

Continuations task<std: : string> water = create_task([] { boil. Water(); return "Water boiled"s; });

Continuations task<std: : string> water = create_task([] { boil. Water(); return "Water boiled"s; }); task<void> tea = water. then( [](const std: : string &msg) { make. Tea(); }); tea. wait(); 14

Обработка исключений auto tea = create_task([]() -> int { BANG! throw std: : runtime_error{

Обработка исключений auto tea = create_task([]() -> int { BANG! throw std: : runtime_error{ "BANG!" }; }). then([](const task<int> &t) { BANG! t. get(); boil. Water(); return "Water boiled"s; }). then([](const std: : string &msg) { make. Tea(); }); BANG! tea. wait(); 15

Cancellation cancellation_token_source token. Source; create_task([token = token. Source. get_token()] { boil. Water(); if (token.

Cancellation cancellation_token_source token. Source; create_task([token = token. Source. get_token()] { boil. Water(); if (token. is_canceled()) cancel_current_task(); //throws task_canceled{} make. Tea(); Использование: }) token. Source. cancel(); . wait(); 16

Cancellation callback void boil. Water(cancellation_token) { const auto registration = token. register_callback([] { stop.

Cancellation callback void boil. Water(cancellation_token) { const auto registration = token. register_callback([] { stop. Boiling(); предложение P 0660 }); в стандарт C++ boil. Water(); token. deregister_callback(registration); } 17

Task composition: when_all task<std: : string> boil. Water. And. Make. Tea. Async(); task<std: :

Task composition: when_all task<std: : string> boil. Water. And. Make. Tea. Async(); task<std: : string> make. Sandwich. Async(); task<std: : string> tasks[] = { boil. Water. And. Make. Tea. Async(), make. Sandwich. Async() возвращает результаты задач }; task<std: : vector<std: : string>> result = when_all(std: : begin(tasks), std: : end(tasks)); 18

Task composition: when_any task<std: : string> boil. Water. And. Make. Tea. Async(); task<std: :

Task composition: when_any task<std: : string> boil. Water. And. Make. Tea. Async(); task<std: : string> make. Sandwich. Async(); task<std: : string> tasks[] = { boil. Water. And. Make. Tea. Async(), make. Sandwich. Async() возвращает результат задачи и её индекс }; task<std: : pair<std: : string, size_t>> result = when_any(std: : begin(tasks), std: : end(tasks)); 19

Идиома: do while template<typename Func> task<void> do. While(Func func) { return create_task(func). then([func](bool need.

Идиома: do while template<typename Func> task<void> do. While(Func func) { return create_task(func). then([func](bool need. To. Continue) { if (need. To. Continue) return do. While(func); return task_from_result(); } 20

Идиома: отмена нескольких запросов task<std: : string> make. Request(const std: : string &request, cancellation_token);

Идиома: отмена нескольких запросов task<std: : string> make. Request(const std: : string &request, cancellation_token); struct Gadget { task<std: : string> make. Request(const std: : string &request) { return make. Request(request, m_token. Source. get_token()); } void cancel. All. Requests() { m_token. Source. cancel(); m_token. Source = {}; } private: cancellation_token_source m_token. Source; }; 21

Идиома: continuation chaining auto avatar = http: : client: : http_client{"https: //reqres. in"}. request(http:

Идиома: continuation chaining auto avatar = http: : client: : http_client{"https: //reqres. in"}. request(http: : methods: : GET, "/api/users/1"). then([](const http: : http_response &response) { if (response. status_code() != http: : status_codes: : OK) throw std: : runtime_error("Failed to get user"); return response. extract_json(); }). then([](const json: : value &response) { const auto url = response. at("data"). at("avatar"). as_string(); return http: : client: : http_client(url). request(http: : methods: : GET); }). then([](const concurrency: : task<http: : http_response> &result) { const auto response = result. get(); if (response. status_code() != http: : status_codes: : OK) throw std: : runtime_error("Failed to get avatar"); return response. extract_vector(); }); 22

https: //isocpp. org/std/status 23

https: //isocpp. org/std/status 23

Зачем мне корутины? task<void> boil. Water. Async(); task<void> make. Tea. Async(); task<std: : string>

Зачем мне корутины? task<void> boil. Water. Async(); task<void> make. Tea. Async(); task<std: : string> boil. Water. And. Make. Tea. Async() { return boil. Water. Async(). then([] { return make. Tea. Async(); }). then([] { return "tea ready"s; }); } 24

Зачем мне корутины? task<void> boil. Water. Async(); task<void> make. Tea. Async(); task<std: : string>

Зачем мне корутины? task<void> boil. Water. Async(); task<void> make. Tea. Async(); task<std: : string> boil. Water. And. Make. Tea. Async() { co_await boil. Water. Async(); } co_await make. Tea. Async(); co_return "tea ready"; 25

auto avatar = http: : client: : http_client{"https: //reqres. in"}. request(http: : methods: :

auto avatar = http: : client: : http_client{"https: //reqres. in"}. request(http: : methods: : GET, "/api/users/1"). then([](const http: : http_response &response) { if (response. status_code() != http: : status_codes: : OK) throw std: : runtime_error("Failed to get user"); return response. extract_json(); }). then([](const json: : value &response) { const auto url = response. at("data"). at("avatar"). as_string(); return http: : client: : http_client(url). request(http: : methods: : GET); }). then([](const concurrency: : task<http: : http_response> &result) { const auto response = result. get(); if (response. status_code() != http: : status_codes: : OK) throw std: : runtime_error("Failed to get avatar"); return response. extract_vector(); }); 26

const http: : http_response user. Response = co_await http: : client: : http_client{"https: //reqres.

const http: : http_response user. Response = co_await http: : client: : http_client{"https: //reqres. in"}. request(http: : methods: : GET, "/api/users/1"); if (user. Response. status_code() != http: : status_codes: : OK) throw std: : runtime_error("Failed to get user"); const json: : value json. Response = co_await user. Response. extract_json(); const auto url = json. Response. at("data"). at("avatar"). as_string(); const http: : http_response avatar. Response = co_await http: : client: : http_client{url}. request(http: : methods: : GET); if (avatar. Response. status_code() != http: : status_codes: : OK) throw std: : runtime_error("Failed to get avatar"); auto avatar = co_await avatar. Response. extract_vector(); 27

Зачем мне генераторы? std: : generator<std: : string> stop. Gort( std: : string suffix)

Зачем мне генераторы? std: : generator<std: : string> stop. Gort( std: : string suffix) { co_yield "Klaatu" + suffix; co_yield "barada" + suffix; co_yield "nikto" + suffix; } auto words = stop. Gort(", please"); for (auto i : words) std: : cout << i << 'n'; 28

Зачем мне генераторы? std: : generator<int> fibonacci() { for (int cur = 0, next

Зачем мне генераторы? std: : generator<int> fibonacci() { for (int cur = 0, next = 1; ; ) { co_yield cur; cur = std: : exchange(next, cur + next); } } for (auto n : fibonacci()) std: : cout << n << 'n'; 29

Что происходит "за кулисами" co_await boil. Water. Async(); из контекста корутины //std: : experimental:

Что происходит "за кулисами" co_await boil. Water. Async(); из контекста корутины //std: : experimental: : coroutine_handle<> coroutine. Handle; auto awaitable = operator co_await(boil. Water. Async()); if (!awaitable. await_ready()) { awaitable. await_suspend(coroutine. Handle); //suspend & resume } запланировать продолжение вернуть значение awaitable. await_resume(); 32

Что происходит "за кулисами" task<std: : string> boil. Water. And. Make. Tea. Async() {

Что происходит "за кулисами" task<std: : string> boil. Water. And. Make. Tea. Async() { co_await boil. Water. Async(); co_await make. Tea. Async(); co_return "tea ready"; } 33

Что происходит "за кулисами" task<std: : string> boil. Water. And. Make. Tea. Async() {

Что происходит "за кулисами" task<std: : string> boil. Water. And. Make. Tea. Async() { auto p = std: : coroutine_traits<task<std: : string>>: : promise_type{}; auto return. Object = p. get_return_object(); co_await p. initial_suspend(); часть контекста корутины try { возвращается на co_await boil. Water. Async(); //suspend & resume первой приостановке co_await make. Tea. Async(); //suspend & resume p. return_value("tea ready"); goto final_suspend; //co_return } catch (. . . ) { p. unhandled_exception(); } final_suspend: co_await p. final_suspend(); 34 }

Что происходит "за кулисами" co_yield expression; co_await promise. yield_value(expression); 35

Что происходит "за кулисами" co_yield expression; co_await promise. yield_value(expression); 35

Gotta go faster 36

Gotta go faster 36

Benchmark: std: : future void future(benchmark: : State& state) { for (auto i :

Benchmark: std: : future void future(benchmark: : State& state) { for (auto i : state) { std: : future<void> water = std: : async(std: : launch: : deferred, [] { boil. Water(); }); auto tea = std: : async(std: : launch: : deferred, [water = std: : move(water)]() mutable { water. get(); make. Tea(); }); время=541 нс tea. get(); скорость=1 x } } BENCHMARK(future); 38

Benchmark: concurrency: : task void concurrency. Task(benchmark: : State& state) { for (auto i

Benchmark: concurrency: : task void concurrency. Task(benchmark: : State& state) { for (auto i : state) { [] { boil. Water(); return concurrency: : task_from_result(); }(). then([] { make. Tea(); }) время=7195 нс. wait(); } скорость=0, 08 x } BENCHMARK(concurrency. Task); 39

Benchmark: легковесный Task boil. Water. Async() { boil. Water(); co_return; } Task make. Tea.

Benchmark: легковесный Task boil. Water. Async() { boil. Water(); co_return; } Task make. Tea. Async() { make. Tea(); co_return; } void coroutines(benchmark: : State& state) { [&state]() -> std: : future<void> { for (auto i : state) { co_await boil. Water. Async(); co_await make. Tea. Async(); время=204 нс } скорость=2, 7 x }(). wait(); } BENCHMARK(coroutines); 40

Benchmark Run on (4 X 3392 MHz CPU s) CPU Caches: L 1 Data

Benchmark Run on (4 X 3392 MHz CPU s) CPU Caches: L 1 Data 32 K (x 4) L 1 Instruction 32 K (x 4) L 2 Unified 262 K (x 4) L 3 Unified 6291 K (x 1) ---------------------------Benchmark Time CPU Iterations ---------------------------future 541 ns 547 ns 1000000 concurrency. Task 7195 ns 7254 ns 112000 coroutines 204 ns 3446154 raw. Calls 1 ns 100000 41

легковесный Task struct Task. Promise { struct Task get_return_object(); bool initial_suspend() { return false;

легковесный Task struct Task. Promise { struct Task get_return_object(); bool initial_suspend() { return false; } auto final_suspend() { struct Awaitable { bool await_ready() { return false; } void await_suspend(std: : coroutine_handle<Task. Promise> coro) { if (auto continuation = coro. promise(). continuation) continuation. resume(); } void await_resume() {} }; return Awaitable{}; } void unhandled_exception() { exception = std: : current_exception(); } void return_void() {} void result() { if (exception) std: : rethrow_exception(exception); } std: : coroutine_handle<> continuation; std: : exception_ptr exception; }; 42

легковесный Task struct [[nodiscard]] Task { using promise_type = Task. Promise; Task(coroutine_handle<Task. Promise> coro)

легковесный Task struct [[nodiscard]] Task { using promise_type = Task. Promise; Task(coroutine_handle<Task. Promise> coro) : m_coro(coro) {} ~Task() { if (m_coro) m_coro. destroy(); } friend auto operator co_await(const Task &t) { struct Awaitable { bool await_ready() { return coro. done(); } void await_suspend(coroutine_handle<> coro) { this->coro. promise(). continuation = coro; } void await_resume() { coro. promise(). result(); } coroutine_handle<Task. Promise> coro; }; return Awaitable{ t. m_coro }; } private: coroutine_handle<Task. Promise> m_coro; }; Task. Promise: : get_return_object() { return Task{ coroutine_handle<Task. Promise>: : from_promise(*this) }; } 43