Код приложений для мобильных устройств, написанный на C++, часто оказывается сложнее аналогичного на Java, ObjC или C#. Данные языки предлагают решения стандартных задач разработчика «из коробки». Многие из этих задач связаны с асинхронным выполнением операций.
В докладе я представлю подход к написанию понятного и производительного асинхронного кода на С++, который применяется в разработке библиотек для мобильных геоприложений в Яндексе.
3. 3
Содержание
! Как работает загрузка карты
! Стейт-машина и линейный код
! С++11 concurrency
! Наш вариант concurrency
! Пример реализации загрузки карты
5. 5
Тайл
! Можно отменять
! Должен кешироваться
! Должен обновляться
! Один тайл может содержать множество версий
6. 6
Схема загрузки одного тайла
Старт
Есть
в памяти?
Сходить в диск
Есть
на диске?
Сходить в сеть
Обновился?
Конец
Вернуть
тайл
Декодировать
и положить в память
Вернуть
тайл
Вернуть
тайл
Декодировать
и положить в память
Нет
Да
Да
Да
Нет
Нет
23. 23
Схема загрузки одного тайла
Старт
Есть
в памяти?
Сходить в диск
Есть
на диске?
Сходить в сеть
Обновился?
Конец
Вернуть
тайл
Декодировать
и положить в память
Вернуть
тайл
Вернуть
тайл
Декодировать
и положить в память
Нет
Да
Да
Да
Нет
Нет
24. 24
Загрузка одного тайла
NetTileProvider
Один сырой тайл
Запрос одной версии
тайла из сети
RawTileLoader
Кеширование на диске
Версионирование
Поток версий сырых данных
TileLoader
Кеширование в памяти
Декодирование
Поток версий готовых тайлов
25. 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!
}!
26. 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!
}!
27. 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);!
}!
}!
!
28. 28
Мы пишем понятный и выразительный код
И вам желаем того жеJ