Дмитрий Жестилевский — Yet another threading framework: асинхронная...

30
1

description

Код приложений для мобильных устройств, написанный на C++, часто оказывается сложнее аналогичного на Java, ObjC или C#. Данные языки предлагают решения стандартных задач разработчика «из коробки». Многие из этих задач связаны с асинхронным выполнением операций. В докладе я представлю подход к написанию понятного и производительного асинхронного кода на С++, который применяется в разработке библиотек для мобильных геоприложений в Яндексе.

Transcript of Дмитрий Жестилевский — Yet another threading framework: асинхронная...

Page 1: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

1

Page 2: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

2

карты

Асинхронная разработка на C++

Дмитрий Жестилевский

Page 3: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

3

Содержание

!  Как работает загрузка карты ! Стейт-машина и линейный код

!  С++11 concurrency !  Наш вариант concurrency

!  Пример реализации загрузки карты

Page 4: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

4

Page 5: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

5

Тайл

!  Можно отменять !  Должен кешироваться !  Должен обновляться !  Один тайл может содержать множество версий

Page 6: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

6

Схема загрузки одного тайла Старт

Обновился?

Есть в памяти?

Есть на диске?

Сходить в диск

Сходить в сеть

Конец

Вернуть тайл

Декодировать и положить в память

Вернуть тайл

Вернуть тайл

Декодировать и положить в память

Нет

Да

Да

Да

Нет

Нет

Page 7: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

7

Конечный автомат Он же стейт-машина

Page 8: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

8

Row<NotInRam , RequestTile , LoadingFromDisk , none , none >, !Row<NotInRam , DiscardTile , none , none , none >, !Row<NotInRam , UpdateTile , none , none , none >, !// +-------------+--------------------------+-----------------+-------------------+-----------------------+ !Row<LoadingFromDisk, RequestTile , none , none , none >, !// this transition will be performed if isTileUpToDate returns false !Row<LoadingFromDisk, TileReady , UpdatingFromNet , NotifyCaller , none >, !Row<LoadingFromDisk, TileReady , UpToDateInRam , NotifyCaller , IsTileUpToDate >, !Row<LoadingFromDisk, TileRequestError , LoadingFromNet , none , none >, !// +-------------+--------------------------+-----------------+-------------------+-----------------------+ !Row<LoadingFromNet , RequestTile , none , none , none >, !Row<LoadingFromNet , TileReady , InRamSyncing , NotifyCaller , none >, !Row<LoadingFromNet , TileRequestError , NotInRam , NotifyCaller , none >, !// +-------------+--------------------------+-----------------+-------------------+-----------------------+ !Row<UpdatingFromNet, RequestTile , none , NotifyCaller , none >, !Row<UpdatingFromNet, TileReady , InRamSyncing , NotifyCaller , none >, !Row<UpdatingFromNet, TileRequestError , OldInRam , none , none >, !// +-------------+--------------------------+-----------------+-------------------+-----------------------+ !Row<InRamSyncing , RequestTile , none , NotifyCaller , none >, !Row<InRamSyncing , DiscardTile , none , none , none >, !Row<InRamSyncing , TileSyncingCompleted , UpToDateInRam , none , none >, !Row<InRamSyncing , TileSyncingError , UpToDateInRam , none , none >, !// +-------------+--------------------------+-----------------+-------------------+-----------------------+ !// May be we don't need this state. !Row<OldInRam , RequestTile , UpdatingFromNet , NotifyCaller , none >, !Row<OldInRam , UpdateTile , UpdatingFromNet , none , none >, !Row<OldInRam , DiscardTile , none , none , none >, !// +-------------+--------------------------+-----------------+-------------------+-----------------------+ !Row<UpToDateInRam , RequestTile , none , NotifyCaller , none >, !Row<UpToDateInRam , UpdateTile , none , none , none >, !Row<UpToDateInRam , UpdateTile , UpdatingFromNet , none , Not_<IsTileUpToDate> >, !Row<UpToDateInRam , DiscardTile , none , none , none >, !// +-------------+--------------------------+-----------------+-------------------+-----------------------+ !Row<AllOk , DiscardTile , CancelledMode , none , none >, !Row<CancelledMode , RequestTile , AllOk , none , none >!

Page 9: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

9

Линейный код

void loadTile(int x, int y, output_iterator output) { ! if (auto tile = inMemory(x, y)) { ! output << tile; ! } !! if (auto tile = readFromDisk(x, y)) { ! output << decode(tile); ! prevVersion = tile.version; ! } !! while (true) { ! Tile tile = readFromNet(x, y, prevVersion); ! if (tile.version == prevVersion) continue; ! prevVersion = tile.version; ! output << decode(tile); ! } !} ! !

Page 10: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

10

C++11 concurrency

Page 11: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

11

Future/Promise

Future Promise

Promise<int> p; !Future<int> f = p.future(); !

promise.set(val); !std::cout << value; !

auto future = startAsync(); !// ... !// ... !int value = future.get(); !

Promise<int> promise; !

int val = calc(); !

Page 12: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

12

Использование Future

int meaningOfLife() { ! sleep(100500); ! return 42; !} !!int main() { ! std::future<int> meaningOfLife = std::async([] { ! return meaningOfLife(); ! }); !! std::future<int> calculation = std::async([] { return calcSmth(); }); !! std::cout << meaningOfLife().get() + calculation.get() << std::endl; !} !

{  calcSmth Meaning

Of Life

}  

output  

Page 13: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

13

std::async

Пример реализации std::async в случае создания отдельного потока на каждую операцию

Future<T> async(Function<T()> func) { ! Promise<T> promise; ! auto future = promise.future(); !! std::thread([promise = std::move(promise), func] { ! try { ! promise.setValue(func()); ! } catch (...) { ! promise.setException(std::current_exception()); ! } ! }).detach(); !! return future; !} !

Page 14: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

14

IO binding

Использование асинхронной сети в синхронном коде

void NetworkingAPI::httpGet(std::string url, ! std::function<void(Response, Error)>);!!Future<Response> httpGet(const std::string& url) { ! Promise<Response> promise; ! auto future = promise.future(); !! NetworkingAPI::httpGet(url, ! [promise = std::move(promise)] ! (Response response, Error error) { ! if (error) ! promise.setException(error); ! else ! promise.setValue(response); ! }); !} !

Page 15: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

15

Чего нет в std::

!  Масштабируемость ! Отменяемость асинхронных операций

!  Генераторы — функции, возвращающие множество результатов

Page 16: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

16

Контексты исполнения

!  Потоки !  Процессы

Page 17: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

17

Coroutine – user-space thread Пишем асинхронный код синхронно

Page 18: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

18

Примитивы

async::Future !async::Promise !

async::Mutex!async::CV !

Coroutines!Coro Scheduler !

std::future !std::promise !

std::mutex!std::cv !

Threads !OS Scheduler !

Future.get() не блокирует поток

Реализованы ~одинаково

Page 19: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

19

Модель потоков

Все взаимодействие с IO – в отдельных выделенных потоках (сеть, диск) Все остальное – в глобальном thread pool

Page 20: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

20

Отмена операций

!  Отмена через exception (Future.reset, ~Future) !  Cancellation points: wait, sleep, get

!  Мгновенная отмена или ленивая?

Page 21: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

21

Генераторы

Page 22: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

22

Поточная десериализация Генератор1: байты Генератор2: байты объекты

MultiFuture<char> networkBytes(); !!MultiFuture<Object> objects(MultiFuture<char> bytesStream) { ! return async<Object>([](MultiPromise<Object>* output) { ! Parser parser(std::move(bytesStream)); !! while (!parser.finished()) { ! output->yield(parser.next()); ! } ! }; !} !!void Parser::read(int size, char* data) { ! while (size > 0) { ! *data = bytes_.get(); ! data++; ! size--; ! } !} !!

Page 23: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

23

Схема загрузки одного тайла Старт

Обновился?

Есть в памяти?

Есть на диске?

Сходить в диск

Сходить в сеть

Конец

Вернуть тайл

Декодировать и положить в память

Вернуть тайл

Вернуть тайл

Декодировать и положить в память

Нет

Да

Да

Да

Нет

Нет

Page 24: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

24

Загрузка одного тайла

NetTileProvider

Один сырой тайл

Запрос одной версии тайла из сети

RawTileLoader

Поток версий сырых данных

Кеширование на диске Версионирование

TileLoader

Поток версий готовых тайлов

Кеширование в памяти Декодирование

Page 25: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

25

RawTileLoader Задача – вернуть поток версий сырых данных

MultiFuture<RawTile> rawTileVersions(int x, y) { ! return async<RawTile>([=](MultiPromise<RawTile>* output) {! std::string currentVer, currentEtag; ! RawTile rawTile; !! if (rawTile = readFromDisk(x, y)) { ! currentVer = rawTile->version; ! currentEtag = rawTile->etag; ! output->yield(rawTile); ! } !! for (const auto& ver : versions_.subscribe()) { ! rawTile = loadFromNetwork(x, y, ver, currentEtag); !! writeToDisk(x, y, rawTile); !! currentVer = rawTile->version; ! currentEtag = rawTile->etag; ! output->yield(rawTile); ! } ! }); // async !} !

Page 26: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

26

TileLoader

Задача – вернуть поток версий одного тайла

MultiFuture<Tile> tileVersions(int x, y) { ! return async<Tile>([=](MultiPromise<Tile>* output) { ! Tile tile = memCache->get(x, y); ! if (tile) ! output->yield(tile); !! for (const auto& rawTile : rawTileVersions(x, y)) { ! tile.version = rawTile->version; ! if (tile.etag != rawTile->etag) { ! tile.etag = rawTile->etag; ! tile.data = decoder_(rawTile->rawData); ! } !! output->yield(tile); ! memCache_->set(x, y, tile); ! } ! }); // async !} !

Page 27: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

27

Линейный код из примера

void loadTile(int x, int y, output_iterator output) { ! if (auto tile = inMemory(x, y)) { ! output << tile; ! } !! if (auto tile = readFromDisk(x, y)) { ! output << decode(tile); ! prevVersion = tile.version; ! } !! while (true) { ! Tile tile = readFromNet(x, y, prevVersion); ! if (tile.version == prevVersion) continue; ! prevVersion = tile.version; ! output << decode(tile); ! } !} ! !

Page 28: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

28

Мы пишем понятный и выразительный код

И вам желаем того жеJ

Page 29: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

29

Спасибо за внимание!

Page 30: Дмитрий Жестилевский — Yet another threading framework: асинхронная разработка на C++ под мобильные устройства

30

Дмитрий Жестилевский

Яндекс.Карты

[email protected]

Руководитель группы

@dr_zhest