SlideShare a Scribd company logo
Евгений Охотников
Коротко о себе
Профессиональный разработчик с го
● АСУ ТП и т п
● телеком платежные сервисы
● инструментарий для
Автор блога Размышлизмы
Сооснователь ⇛
Автор ряда докладов про Модель Акторов и С
2
Зачем нужен этот доклад?
Чтобы показать насколько
не страшно выглядит
многопоточное программирование с
использованием высокоуровневых
моделей
3
Не грех повторить еще раз:
Многопоточное программирование на
посредством голых нитей ов и
это пот боль и кровь
4
Можно не верить мне...
5
Архитектура мета
сервера мобильного
онлайн шутера
Можно не верить мне...
6
Архитектура мета
сервера мобильного
онлайн шутера
Через пару недель потраченных на
поиск и исправление наиболее
критических багов мы решили что
проще переписать все с нуля чем
пытаться исправить все недостатки
текущего решения
Почему мы ходим по граблям?
Незнание
Лень
синдром
7
Есть много чего
Проверено временем и множеством проектов
●
●
●
●
●
●
Этому вряд ли учат в ВУЗ ах
8
Простая задача
сервер который
● принимает запрос картинки пользователя
● отдает картинку с водяными знаками уникальными для этого
пользователя
9
Что нужно делать?
10
client
our service
user database
image storage
Конкурентность напрашивается...
11
Набор рабочих нитей
12
Http Server
Request
Processing
Http Client
(outgoing
requests)
Image Processing
Disclaimer
Примеры не привязаны к какому то конкретному инструменту
Везде есть некий
13
Попытка №1
14
Модель Акторов. Основные принципы
● актор это некая сущность обладающая поведением
● акторы реагируют на входящие сообщения
● получив сообщение актор может
○ отослать некоторое конечное количество сообщений
другим акторам
○ создать некоторое конечное количество новых акторов
○ определить для себя новое поведение для обработки
последующих сообщений
15
Модель Акторов. Основные принципы
● актор это некая сущность обладающая поведением
● акторы реагируют на входящие сообщения
● получив сообщение актор может
○ отослать некоторое конечное количество сообщений
другим акторам
○ создать некоторое конечное количество новых акторов
○ определить для себя новое поведение для обработки
последующих сообщений
16
Могут быть реализованы сильно по разному
● каждый актор это отдельный поток ОС
● каждый актор это сопрограмма
● каждый актор это объект у которого кто то
вызывает коллбэки
Сейчас будем говорить про акторов виде объектов с
коллбэками
Сопрограммы оставим для разговора про
Один из возможных вариантов
17
Актор request_handler (1)
class request_handler final : public some_basic_type {
const execution_context context_;
const request request_;
optional<user_info> user_info_;
optional<image_loaded> image_;
void on_start();
void on_user_info(user_info info);
void on_image_loaded(image_loaded image);
void on_mixed_image(mixed_image image);
void send_mix_images_request();
... // вся специфическая для фреймворка обвязка.
};
18
Актор request_handler (2)
void request_handler::on_start() {
send(context_.user_checker(), check_user{request_.user_id(), self()});
send(context_.image_downloader(), download_image{request_.image_id(), self()});
}
19
Актор request_handler (3)
void request_handler::on_user_info(user_info info) {
user_info_ = std::move(info);
if(image_)
send_mix_images_request();
}
void request_handler::on_image_loaded(image_loaded image) {
image_ = std::move(image);
if(user_info_)
send_mix_images_request();
}
20
Актор request_handler (4)
void request_handler::send_mix_images_request() {
send(context_.image_mixer(),
mix_images{user_info->watermark_image(), *image_, self()});
}
void request_handler::on_mixed_image(mixed_image image) {
send(context_.http_srv(), reply{..., std::move(image), ...});
}
21
Особенности Модели Акторов (1)
Акторы реактивны
Работают при наличии входящих сообщений
22
Особенности Модели Акторов (2)
Акторы подвержены перегрузкам
Следствие асинхронной природы взаимодействия
не блокирует отправителя
23
Особенности Модели Акторов (3)
Много акторов ≠ решение
Зачастую много акторов ⇒ еще одна проблема
24
Куда смотреть, что брать?
25
Поддержи отечественного
производителя
Попытка №2
26
CSP на пальцах и без матана
Композиция параллельно работающих последовательных
процессов
Взаимодействие посредством асинхронных сообщений
Сообщения доставляются посредством каналов
У каналов есть две операции и
27
CSP на пальцах и без матана
28
Композиция параллельно работающих последовательных
процессов
Взаимодействие посредством асинхронных сообщений
Сообщения доставляются посредством каналов
У каналов есть две операции и
Последовательные процессы внутри одного
процесса ОС могут быть реализованы как
● отдельные нити ОС В этом случае имеем
вытесняющую многозадачность
● сопрограммы
В этом случае имеем кооперативную
многозадачность
Один из возможных вариантов
29
"Процесс" request_handler
void request_handler(const execution_context ctx, const request req)
{
auto user_info_ch = make_chain<user_info>();
auto image_loaded_ch = make_chain<image_loaded>();
ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch});
ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch});
auto user = user_info_ch.read();
auto original_image = image_loaded_ch.read();
auto image_mix_ch = make_chain<mixed_image>();
ctx.image_mixer_ch().write(
mix_images{user.watermark_image(), std::move(original_image), image_mix_ch});
auto result_image = image_mix_ch.read();
ctx.http_srv_ch().write(reply{..., std::move(result_image), ...});
}
30
"Процесс" request_handler
void request_handler(const execution_context ctx, const request req)
{
auto user_info_ch = make_chain<user_info>();
auto image_loaded_ch = make_chain<image_loaded>();
ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch});
ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch});
auto user = user_info_ch.read();
auto original_image = image_loaded_ch.read();
auto image_mix_ch = make_chain<mixed_image>();
ctx.image_mixer_ch().write(
mix_images{user.watermark_image(), std::move(original_image), image_mix_ch});
auto result_image = image_mix_ch.read();
ctx.http_srv_ch().write(reply{..., std::move(result_image), ...});
}
31
"Процесс" request_handler
void request_handler(const execution_context ctx, const request req)
{
auto user_info_ch = make_chain<user_info>();
auto image_loaded_ch = make_chain<image_loaded>();
ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch});
ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch});
auto user = user_info_ch.read();
auto original_image = image_loaded_ch.read();
auto image_mix_ch = make_chain<mixed_image>();
ctx.image_mixer_ch().write(
mix_images{user.watermark_image(), std::move(original_image), image_mix_ch});
auto result_image = image_mix_ch.read();
ctx.http_srv_ch().write(reply{..., std::move(result_image), ...});
}
32
"Процесс" request_handler
void request_handler(const execution_context ctx, const request req)
{
auto user_info_ch = make_chain<user_info>();
auto image_loaded_ch = make_chain<image_loaded>();
ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch});
ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch});
auto user = user_info_ch.read();
auto original_image = image_loaded_ch.read();
auto image_mix_ch = make_chain<mixed_image>();
ctx.image_mixer_ch().write(
mix_images{user.watermark_image(), std::move(original_image), image_mix_ch});
auto result_image = image_mix_ch.read();
ctx.http_srv_ch().write(reply{..., std::move(result_image), ...});
}
33
"Процесс" request_handler
void request_handler(const execution_context ctx, const request req)
{
auto user_info_ch = make_chain<user_info>();
auto image_loaded_ch = make_chain<image_loaded>();
ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch});
ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch});
auto user = user_info_ch.read();
auto original_image = image_loaded_ch.read();
auto image_mix_ch = make_chain<mixed_image>();
ctx.image_mixer_ch().write(
mix_images{user.watermark_image(), std::move(original_image), image_mix_ch});
auto result_image = image_mix_ch.read();
ctx.http_srv_ch().write(reply{..., std::move(result_image), ...});
}
34
"Процесс" request_handler
void request_handler(const execution_context ctx, const request req)
{
auto user_info_ch = make_chain<user_info>();
auto image_loaded_ch = make_chain<image_loaded>();
ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch});
ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch});
auto user = user_info_ch.read();
auto original_image = image_loaded_ch.read();
auto image_mix_ch = make_chain<mixed_image>();
ctx.image_mixer_ch().write(
mix_images{user.watermark_image(), std::move(original_image), image_mix_ch});
auto result_image = image_mix_ch.read();
ctx.http_srv_ch().write(reply{..., std::move(result_image), ...});
}
35
"Процесс" request_handler
void request_handler(const execution_context ctx, const request req)
{
auto user_info_ch = make_chain<user_info>();
auto image_loaded_ch = make_chain<image_loaded>();
ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch});
ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch});
auto user = user_info_ch.read();
auto original_image = image_loaded_ch.read();
auto image_mix_ch = make_chain<mixed_image>();
ctx.image_mixer_ch().write(
mix_images{user.watermark_image(), std::move(original_image), image_mix_ch});
auto result_image = image_mix_ch.read();
ctx.http_srv_ch().write(reply{..., std::move(result_image), ...});
}
36
"Процесс" request_handler
void request_handler(const execution_context ctx, const request req)
{
auto user_info_ch = make_chain<user_info>();
auto image_loaded_ch = make_chain<image_loaded>();
ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch});
ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch});
auto user = user_info_ch.read();
auto original_image = image_loaded_ch.read();
auto image_mix_ch = make_chain<mixed_image>();
ctx.image_mixer_ch().write(
mix_images{user.watermark_image(), std::move(original_image), image_mix_ch});
auto result_image = image_mix_ch.read();
ctx.http_srv_ch().write(reply{..., std::move(result_image), ...});
}
37
Особенности CSP (1)
Процессы могут вести себя как захотят быть реактивными
проактивными или чередовать реактивность с проактивностью
Процессы могут работать даже в отсутствии сообщений
38
Особенности CSP (2)
Родные механизмы защиты от перегрузки
Каналы могут быть ограничены по размеру
Где то только такими и могут быть
Отправитель блокируется при записи в полный канал
39
Особенности CSP (3)
Процессы в виде нитей ОС ⇒ дорого
Плата за стек Плата за переключение
Но вытесняющая многозадачность
Процессы в виде сопрограмм ⇒ дешево
Но кооперативная многозадачность
Но сюрпризы с например
40
Особенности CSP (4)
Много процессов ≠ решение
Зачастую много процессов ⇒ еще одна проблема
41
Куда смотреть, что брать?
42
Actors vs CSP
один единственный канал
только реактивность
могут быть сложными конечными автоматами
шные процессы
много каналов
и реактивность и проактивность
не очень хорошо с конечными автоматами
лучше когда поддерживаются на уровне языка и
43
Попытка №3
44
Про Task-based подход в двух словах
Решение строится как набор небольших задач
Каждая задача выполняет одну операцию
Задачи запускаются вызовами Результатом является
Задачи провязываются в цепочки посредством
45
Реализация обработки запроса (1)
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
46
Реализация обработки запроса (2)
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
47
Реализация обработки запроса (3)
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
48
Реализация обработки запроса (4)
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
49
Реализация обработки запроса (5)
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
50
Реализация обработки запроса (6)
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
51
Последовательность операций
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
52
1
2
3
4
5
Последовательность операций
53
1
2
3
4
5
0
Загрузка
Загрузка изображения
Последовательность операций
54
1
2
3
4
5
0
Загрузка
Загрузка изображения
Есть и
изображение и
Последовательность операций
55
1
2
3
4
5
0
Загрузка
Загрузка изображения
Есть и
изображение и
Микширование
изображения
Последовательность операций
56
1
2
3
4
5
0
Загрузка
Загрузка изображения
Есть и
изображение и
Микширование
изображения
Изображение
смикшировано
Последовательность операций
57
1
2
3
4
5
0
Загрузка
Загрузка изображения
Есть и
изображение и
Микширование
изображения
Изображение
смикшировано
Формирование
ответа на запрос
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
Последовательность операций
58
1
2
3
4
5
0
Особенности Task-ов (1)
Обозримость С ней не очень хорошо
И вот это вот все
59
Особенности Task-ов (2)
Обработка ошибок
60
Особенности Task-ов (3)
Отмена ов
Таймеры таймауты
61
Читинг
void handle_request(const execution_context & ctx, request req)
{
auto user_info_ft = async(ctx.http_client_ctx(),
[req] { return retrieve_user_info(req.user_id()); });
auto original_image_ft = async(ctx.http_client_ctx(),
[req] { return download_image(req.image_id()); });
when_all(user_info_ft, original_image_ft).then(
[&ctx, req](tuple<future<user_info>, future<image_loaded>> data) {
async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] {
return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get());
})
.then([req](future<mixed_image> mixed) {
async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); });
});
});
}
62
Actors/CSP vs Tasks
акцент на обмене информацией
акцент на выполняемых действиях
63
Куда смотреть, что брать?
С
64
Еще интересное
Антон Полухин
Готовимся к С на реальном примере
65
And now, the end is near...
Пора переходить к итогам
66
Забудьте про голую многопоточность
67
Голой многопоточности есть место только внутри фреймворков и
библиотек
Есть из чего выбирать
Проверено временем и множеством проектов
●
●
●
●
●
●
Для всего этого в есть готовые инструменты в том числе и
хорошие
68
The (Happy?) End
Спасибо за внимание
69

More Related Content

PDF
C++ CoreHard Autumn 2018. Actors vs CSP vs Tasks vs ... - Евгений Охотников
PDF
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
PPTX
создание живых сайтов
PDF
Aspect Oriented Approach
PDF
MVVM в WinForms – DevExpress Way (теория и практика)
PDF
Паттерны быстрой разработки WPF MVVM бизнес-приложений
PDF
"Рекомендации по проектированию API" — Марина Степанова, Яндекс
PDF
RequireJS і Magento 2
C++ CoreHard Autumn 2018. Actors vs CSP vs Tasks vs ... - Евгений Охотников
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
создание живых сайтов
Aspect Oriented Approach
MVVM в WinForms – DevExpress Way (теория и практика)
Паттерны быстрой разработки WPF MVVM бизнес-приложений
"Рекомендации по проектированию API" — Марина Степанова, Яндекс
RequireJS і Magento 2

What's hot (10)

PDF
Инструменты разные нужны, инструменты разные важны
PDF
"Погружение в Robolectric" Дмитрий Костырев (Avito)
PDF
Фундаментальные основы разработки под iOS. Павел Тайкало
PDF
Сергей Константинов — Что интересного готовит нам W3C
PDF
JavaScript Базовый. Занятие 09.
PPTX
Создание графического интерфейса пользователя мобильных Android приложений (ч...
PDF
C# Web. Занятие 11.
PPT
Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.
PPTX
Парсим и кодогенерируем для С++ с использованием clang
PPTX
Самодельная параметризация и параллелизация тестов на Webdriver (JS)
Инструменты разные нужны, инструменты разные важны
"Погружение в Robolectric" Дмитрий Костырев (Avito)
Фундаментальные основы разработки под iOS. Павел Тайкало
Сергей Константинов — Что интересного готовит нам W3C
JavaScript Базовый. Занятие 09.
Создание графического интерфейса пользователя мобильных Android приложений (ч...
C# Web. Занятие 11.
Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.
Парсим и кодогенерируем для С++ с использованием clang
Самодельная параметризация и параллелизация тестов на Webdriver (JS)
Ad

Similar to [C++ CoreHard Autumn 2018] Actors vs CSP vs Task... (20)

PDF
Actors for fun and profit
PDF
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
PDF
Cocaine: сценарии использования (Дмитрий Унковский, Яндекс)
PDF
Для чего мы делали свой акторный фреймворк и что из этого вышло?
POTX
Как жить в согласии с SOLID?
PDF
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
PDF
Модель акторов и C++ что, зачем и как?
PDF
модель акторов и C++ что, зачем и как ?
PDF
шишки, набитые за 15 лет использования акторов в c++ v.001.3
PDF
Шишки, набитые за 15 лет использования акторов в C++
PDF
Многопоточность в браузере. Модель акторов — Константин Крамлих
PDF
Akka: как я перестал бояться и полюбил асинхронный код
PDF
"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen
PDF
Веб-сервер Phantom
PPTX
Введение в Akka
PPTX
04 Архитектура информационных систем. Архитектурные модели и стили
POTX
Разработка надежных параллельных, распределенных приложений: быстро и дешево
PDF
Maksym Bezuglyi "Universal highload patterns on a specific example of a game ...
PPTX
Платформа SmartActors
PDF
Анатомия веб-сервиса, Андрей Смирнов
Actors for fun and profit
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
Cocaine: сценарии использования (Дмитрий Унковский, Яндекс)
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Как жить в согласии с SOLID?
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
Модель акторов и C++ что, зачем и как?
модель акторов и C++ что, зачем и как ?
шишки, набитые за 15 лет использования акторов в c++ v.001.3
Шишки, набитые за 15 лет использования акторов в C++
Многопоточность в браузере. Модель акторов — Константин Крамлих
Akka: как я перестал бояться и полюбил асинхронный код
"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen
Веб-сервер Phantom
Введение в Akka
04 Архитектура информационных систем. Архитектурные модели и стили
Разработка надежных параллельных, распределенных приложений: быстро и дешево
Maksym Bezuglyi "Universal highload patterns on a specific example of a game ...
Платформа SmartActors
Анатомия веб-сервиса, Андрей Смирнов
Ad

More from Yauheni Akhotnikau (20)

PDF
arataga. SObjectizer and RESTinio in action: a real-world example
PDF
Actor Model and C++: what, why and how? (March 2020 Edition)
PDF
What is SObjectizer 5.7 (at v.5.7.0)
PDF
What is SObjectizer 5.6 (at v.5.6.0)
PDF
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
PDF
Акторы на C++: стоило ли оно того?
PDF
25 Years of C++ History Flashed in Front of My Eyes
PDF
GECon 2017: C++ - a Monster that no one likes but that will outlast them all
PDF
Dive into SObjectizer 5.5. Tenth part: Mutable Messages
PDF
Actor Model and C++: what, why and how?
PDF
Dive into SObjectizer 5.5. Ninth Part: Message Chains
PDF
Dive into SObjectizer 5.5. Eighth Part: Dispatchers
PDF
What's new in SObjectizer 5.5.9
PDF
Dive into SObjectizer 5.5. Seventh part: Message Limits
PDF
Dive into SObjectizer-5.5. Sixth part: Synchronous Interaction
PDF
Dive into SObjectizer 5.5. Fifth part: Timers
PDF
What’s new in SObjectizer 5.5.8
PDF
Dive into SObjectizer 5.5. Fourth part. Exception
PDF
Dive into SObjectizer 5.5. Third part. Coops
PDF
Dive into SObjectizer 5.5. Introductory part
arataga. SObjectizer and RESTinio in action: a real-world example
Actor Model and C++: what, why and how? (March 2020 Edition)
What is SObjectizer 5.7 (at v.5.7.0)
What is SObjectizer 5.6 (at v.5.6.0)
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
Акторы на C++: стоило ли оно того?
25 Years of C++ History Flashed in Front of My Eyes
GECon 2017: C++ - a Monster that no one likes but that will outlast them all
Dive into SObjectizer 5.5. Tenth part: Mutable Messages
Actor Model and C++: what, why and how?
Dive into SObjectizer 5.5. Ninth Part: Message Chains
Dive into SObjectizer 5.5. Eighth Part: Dispatchers
What's new in SObjectizer 5.5.9
Dive into SObjectizer 5.5. Seventh part: Message Limits
Dive into SObjectizer-5.5. Sixth part: Synchronous Interaction
Dive into SObjectizer 5.5. Fifth part: Timers
What’s new in SObjectizer 5.5.8
Dive into SObjectizer 5.5. Fourth part. Exception
Dive into SObjectizer 5.5. Third part. Coops
Dive into SObjectizer 5.5. Introductory part

[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...

  • 2. Коротко о себе Профессиональный разработчик с го ● АСУ ТП и т п ● телеком платежные сервисы ● инструментарий для Автор блога Размышлизмы Сооснователь ⇛ Автор ряда докладов про Модель Акторов и С 2
  • 3. Зачем нужен этот доклад? Чтобы показать насколько не страшно выглядит многопоточное программирование с использованием высокоуровневых моделей 3
  • 4. Не грех повторить еще раз: Многопоточное программирование на посредством голых нитей ов и это пот боль и кровь 4
  • 5. Можно не верить мне... 5 Архитектура мета сервера мобильного онлайн шутера
  • 6. Можно не верить мне... 6 Архитектура мета сервера мобильного онлайн шутера Через пару недель потраченных на поиск и исправление наиболее критических багов мы решили что проще переписать все с нуля чем пытаться исправить все недостатки текущего решения
  • 7. Почему мы ходим по граблям? Незнание Лень синдром 7
  • 8. Есть много чего Проверено временем и множеством проектов ● ● ● ● ● ● Этому вряд ли учат в ВУЗ ах 8
  • 9. Простая задача сервер который ● принимает запрос картинки пользователя ● отдает картинку с водяными знаками уникальными для этого пользователя 9
  • 10. Что нужно делать? 10 client our service user database image storage
  • 12. Набор рабочих нитей 12 Http Server Request Processing Http Client (outgoing requests) Image Processing
  • 13. Disclaimer Примеры не привязаны к какому то конкретному инструменту Везде есть некий 13
  • 15. Модель Акторов. Основные принципы ● актор это некая сущность обладающая поведением ● акторы реагируют на входящие сообщения ● получив сообщение актор может ○ отослать некоторое конечное количество сообщений другим акторам ○ создать некоторое конечное количество новых акторов ○ определить для себя новое поведение для обработки последующих сообщений 15
  • 16. Модель Акторов. Основные принципы ● актор это некая сущность обладающая поведением ● акторы реагируют на входящие сообщения ● получив сообщение актор может ○ отослать некоторое конечное количество сообщений другим акторам ○ создать некоторое конечное количество новых акторов ○ определить для себя новое поведение для обработки последующих сообщений 16 Могут быть реализованы сильно по разному ● каждый актор это отдельный поток ОС ● каждый актор это сопрограмма ● каждый актор это объект у которого кто то вызывает коллбэки Сейчас будем говорить про акторов виде объектов с коллбэками Сопрограммы оставим для разговора про
  • 17. Один из возможных вариантов 17
  • 18. Актор request_handler (1) class request_handler final : public some_basic_type { const execution_context context_; const request request_; optional<user_info> user_info_; optional<image_loaded> image_; void on_start(); void on_user_info(user_info info); void on_image_loaded(image_loaded image); void on_mixed_image(mixed_image image); void send_mix_images_request(); ... // вся специфическая для фреймворка обвязка. }; 18
  • 19. Актор request_handler (2) void request_handler::on_start() { send(context_.user_checker(), check_user{request_.user_id(), self()}); send(context_.image_downloader(), download_image{request_.image_id(), self()}); } 19
  • 20. Актор request_handler (3) void request_handler::on_user_info(user_info info) { user_info_ = std::move(info); if(image_) send_mix_images_request(); } void request_handler::on_image_loaded(image_loaded image) { image_ = std::move(image); if(user_info_) send_mix_images_request(); } 20
  • 21. Актор request_handler (4) void request_handler::send_mix_images_request() { send(context_.image_mixer(), mix_images{user_info->watermark_image(), *image_, self()}); } void request_handler::on_mixed_image(mixed_image image) { send(context_.http_srv(), reply{..., std::move(image), ...}); } 21
  • 22. Особенности Модели Акторов (1) Акторы реактивны Работают при наличии входящих сообщений 22
  • 23. Особенности Модели Акторов (2) Акторы подвержены перегрузкам Следствие асинхронной природы взаимодействия не блокирует отправителя 23
  • 24. Особенности Модели Акторов (3) Много акторов ≠ решение Зачастую много акторов ⇒ еще одна проблема 24
  • 25. Куда смотреть, что брать? 25 Поддержи отечественного производителя
  • 27. CSP на пальцах и без матана Композиция параллельно работающих последовательных процессов Взаимодействие посредством асинхронных сообщений Сообщения доставляются посредством каналов У каналов есть две операции и 27
  • 28. CSP на пальцах и без матана 28 Композиция параллельно работающих последовательных процессов Взаимодействие посредством асинхронных сообщений Сообщения доставляются посредством каналов У каналов есть две операции и Последовательные процессы внутри одного процесса ОС могут быть реализованы как ● отдельные нити ОС В этом случае имеем вытесняющую многозадачность ● сопрограммы В этом случае имеем кооперативную многозадачность
  • 29. Один из возможных вариантов 29
  • 30. "Процесс" request_handler void request_handler(const execution_context ctx, const request req) { auto user_info_ch = make_chain<user_info>(); auto image_loaded_ch = make_chain<image_loaded>(); ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch}); ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch}); auto user = user_info_ch.read(); auto original_image = image_loaded_ch.read(); auto image_mix_ch = make_chain<mixed_image>(); ctx.image_mixer_ch().write( mix_images{user.watermark_image(), std::move(original_image), image_mix_ch}); auto result_image = image_mix_ch.read(); ctx.http_srv_ch().write(reply{..., std::move(result_image), ...}); } 30
  • 31. "Процесс" request_handler void request_handler(const execution_context ctx, const request req) { auto user_info_ch = make_chain<user_info>(); auto image_loaded_ch = make_chain<image_loaded>(); ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch}); ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch}); auto user = user_info_ch.read(); auto original_image = image_loaded_ch.read(); auto image_mix_ch = make_chain<mixed_image>(); ctx.image_mixer_ch().write( mix_images{user.watermark_image(), std::move(original_image), image_mix_ch}); auto result_image = image_mix_ch.read(); ctx.http_srv_ch().write(reply{..., std::move(result_image), ...}); } 31
  • 32. "Процесс" request_handler void request_handler(const execution_context ctx, const request req) { auto user_info_ch = make_chain<user_info>(); auto image_loaded_ch = make_chain<image_loaded>(); ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch}); ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch}); auto user = user_info_ch.read(); auto original_image = image_loaded_ch.read(); auto image_mix_ch = make_chain<mixed_image>(); ctx.image_mixer_ch().write( mix_images{user.watermark_image(), std::move(original_image), image_mix_ch}); auto result_image = image_mix_ch.read(); ctx.http_srv_ch().write(reply{..., std::move(result_image), ...}); } 32
  • 33. "Процесс" request_handler void request_handler(const execution_context ctx, const request req) { auto user_info_ch = make_chain<user_info>(); auto image_loaded_ch = make_chain<image_loaded>(); ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch}); ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch}); auto user = user_info_ch.read(); auto original_image = image_loaded_ch.read(); auto image_mix_ch = make_chain<mixed_image>(); ctx.image_mixer_ch().write( mix_images{user.watermark_image(), std::move(original_image), image_mix_ch}); auto result_image = image_mix_ch.read(); ctx.http_srv_ch().write(reply{..., std::move(result_image), ...}); } 33
  • 34. "Процесс" request_handler void request_handler(const execution_context ctx, const request req) { auto user_info_ch = make_chain<user_info>(); auto image_loaded_ch = make_chain<image_loaded>(); ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch}); ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch}); auto user = user_info_ch.read(); auto original_image = image_loaded_ch.read(); auto image_mix_ch = make_chain<mixed_image>(); ctx.image_mixer_ch().write( mix_images{user.watermark_image(), std::move(original_image), image_mix_ch}); auto result_image = image_mix_ch.read(); ctx.http_srv_ch().write(reply{..., std::move(result_image), ...}); } 34
  • 35. "Процесс" request_handler void request_handler(const execution_context ctx, const request req) { auto user_info_ch = make_chain<user_info>(); auto image_loaded_ch = make_chain<image_loaded>(); ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch}); ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch}); auto user = user_info_ch.read(); auto original_image = image_loaded_ch.read(); auto image_mix_ch = make_chain<mixed_image>(); ctx.image_mixer_ch().write( mix_images{user.watermark_image(), std::move(original_image), image_mix_ch}); auto result_image = image_mix_ch.read(); ctx.http_srv_ch().write(reply{..., std::move(result_image), ...}); } 35
  • 36. "Процесс" request_handler void request_handler(const execution_context ctx, const request req) { auto user_info_ch = make_chain<user_info>(); auto image_loaded_ch = make_chain<image_loaded>(); ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch}); ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch}); auto user = user_info_ch.read(); auto original_image = image_loaded_ch.read(); auto image_mix_ch = make_chain<mixed_image>(); ctx.image_mixer_ch().write( mix_images{user.watermark_image(), std::move(original_image), image_mix_ch}); auto result_image = image_mix_ch.read(); ctx.http_srv_ch().write(reply{..., std::move(result_image), ...}); } 36
  • 37. "Процесс" request_handler void request_handler(const execution_context ctx, const request req) { auto user_info_ch = make_chain<user_info>(); auto image_loaded_ch = make_chain<image_loaded>(); ctx.user_checker_ch().write(check_user{req.user_id(), user_info_ch}); ctx.image_downloader_ch().write(download_image{req.image_id(), image_loaded_ch}); auto user = user_info_ch.read(); auto original_image = image_loaded_ch.read(); auto image_mix_ch = make_chain<mixed_image>(); ctx.image_mixer_ch().write( mix_images{user.watermark_image(), std::move(original_image), image_mix_ch}); auto result_image = image_mix_ch.read(); ctx.http_srv_ch().write(reply{..., std::move(result_image), ...}); } 37
  • 38. Особенности CSP (1) Процессы могут вести себя как захотят быть реактивными проактивными или чередовать реактивность с проактивностью Процессы могут работать даже в отсутствии сообщений 38
  • 39. Особенности CSP (2) Родные механизмы защиты от перегрузки Каналы могут быть ограничены по размеру Где то только такими и могут быть Отправитель блокируется при записи в полный канал 39
  • 40. Особенности CSP (3) Процессы в виде нитей ОС ⇒ дорого Плата за стек Плата за переключение Но вытесняющая многозадачность Процессы в виде сопрограмм ⇒ дешево Но кооперативная многозадачность Но сюрпризы с например 40
  • 41. Особенности CSP (4) Много процессов ≠ решение Зачастую много процессов ⇒ еще одна проблема 41
  • 43. Actors vs CSP один единственный канал только реактивность могут быть сложными конечными автоматами шные процессы много каналов и реактивность и проактивность не очень хорошо с конечными автоматами лучше когда поддерживаются на уровне языка и 43
  • 45. Про Task-based подход в двух словах Решение строится как набор небольших задач Каждая задача выполняет одну операцию Задачи запускаются вызовами Результатом является Задачи провязываются в цепочки посредством 45
  • 46. Реализация обработки запроса (1) void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } 46
  • 47. Реализация обработки запроса (2) void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } 47
  • 48. Реализация обработки запроса (3) void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } 48
  • 49. Реализация обработки запроса (4) void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } 49
  • 50. Реализация обработки запроса (5) void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } 50
  • 51. Реализация обработки запроса (6) void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } 51
  • 52. Последовательность операций void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } 52 1 2 3 4 5
  • 56. Последовательность операций 56 1 2 3 4 5 0 Загрузка Загрузка изображения Есть и изображение и Микширование изображения Изображение смикшировано
  • 57. Последовательность операций 57 1 2 3 4 5 0 Загрузка Загрузка изображения Есть и изображение и Микширование изображения Изображение смикшировано Формирование ответа на запрос
  • 58. void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } Последовательность операций 58 1 2 3 4 5 0
  • 59. Особенности Task-ов (1) Обозримость С ней не очень хорошо И вот это вот все 59
  • 61. Особенности Task-ов (3) Отмена ов Таймеры таймауты 61
  • 62. Читинг void handle_request(const execution_context & ctx, request req) { auto user_info_ft = async(ctx.http_client_ctx(), [req] { return retrieve_user_info(req.user_id()); }); auto original_image_ft = async(ctx.http_client_ctx(), [req] { return download_image(req.image_id()); }); when_all(user_info_ft, original_image_ft).then( [&ctx, req](tuple<future<user_info>, future<image_loaded>> data) { async(ctx.image_mixer_ctx(), [&ctx, req, d=std::move(data)] { return mix_image(get<0>(d).get().watermark_image(), get<1>(d).get()); }) .then([req](future<mixed_image> mixed) { async(ctx.http_srv_ctx(), [req, im=std::move(mixed)] { make_reply(...); }); }); }); } 62
  • 63. Actors/CSP vs Tasks акцент на обмене информацией акцент на выполняемых действиях 63
  • 65. Еще интересное Антон Полухин Готовимся к С на реальном примере 65
  • 66. And now, the end is near... Пора переходить к итогам 66
  • 67. Забудьте про голую многопоточность 67 Голой многопоточности есть место только внутри фреймворков и библиотек
  • 68. Есть из чего выбирать Проверено временем и множеством проектов ● ● ● ● ● ● Для всего этого в есть готовые инструменты в том числе и хорошие 68
  • 69. The (Happy?) End Спасибо за внимание 69