Post on 15-Jan-2015
description
transcript
1
2
карты
Асинхронная разработка на C++
Дмитрий Жестилевский
3
Содержание
! Как работает загрузка карты ! Стейт-машина и линейный код
! С++11 concurrency ! Наш вариант concurrency
! Пример реализации загрузки карты
4
5
Тайл
! Можно отменять ! Должен кешироваться ! Должен обновляться ! Один тайл может содержать множество версий
6
Схема загрузки одного тайла Старт
Обновился?
Есть в памяти?
Есть на диске?
Сходить в диск
Сходить в сеть
Конец
Вернуть тайл
Декодировать и положить в память
Вернуть тайл
Вернуть тайл
Декодировать и положить в память
Нет
Да
Да
Да
Нет
Нет
7
Конечный автомат Он же стейт-машина
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 >!
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); ! } !} ! !
10
C++11 concurrency
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(); !
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
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; !} !
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); ! }); !} !
15
Чего нет в std::
! Масштабируемость ! Отменяемость асинхронных операций
! Генераторы — функции, возвращающие множество результатов
16
Контексты исполнения
! Потоки ! Процессы
17
Coroutine – user-space thread Пишем асинхронный код синхронно
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() не блокирует поток
Реализованы ~одинаково
19
Модель потоков
Все взаимодействие с IO – в отдельных выделенных потоках (сеть, диск) Все остальное – в глобальном thread pool
20
Отмена операций
! Отмена через exception (Future.reset, ~Future) ! Cancellation points: wait, sleep, get
! Мгновенная отмена или ленивая?
21
Генераторы
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--; ! } !} !!
23
Схема загрузки одного тайла Старт
Обновился?
Есть в памяти?
Есть на диске?
Сходить в диск
Сходить в сеть
Конец
Вернуть тайл
Декодировать и положить в память
Вернуть тайл
Вернуть тайл
Декодировать и положить в память
Нет
Да
Да
Да
Нет
Нет
24
Загрузка одного тайла
NetTileProvider
Один сырой тайл
Запрос одной версии тайла из сети
RawTileLoader
Поток версий сырых данных
Кеширование на диске Версионирование
TileLoader
Поток версий готовых тайлов
Кеширование в памяти Декодирование
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
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
Линейный код из примера
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
Мы пишем понятный и выразительный код
И вам желаем того жеJ
29
Спасибо за внимание!
30
Дмитрий Жестилевский
Яндекс.Карты
gordon@yandex-team.ru
Руководитель группы
@dr_zhest