Практика показывает, что использование подхода, основанного на колбеках для асинхронного программирования обычно не является удобным и подвержено различным ошибкам. Для упрощения написания и поддержки сложных асинхронных программ можно использовать несколько иной подход: использовать сопрограммы для переключения контекста на время ожидания события. Такой подход позволяет реализовать интересные неблокирующие примитивы, включая неблокирующее сетевое взаимодействие, неблокирующие мьютексы, а также удобное переключение между различными пулами потоков для разнесения выполнения задач, которые требуют различные ресурсы.
8. Асинхронный сервер: обсуждение
8
▌ Плюсы:
› Производительность
› Автоматическая параллелизация исполнения
▌ Минусы:
› Сложность:
1. Нелинейный рост сложности и проблем
2. Явная передача контекста исполнения
3. Обработка ошибок
4. Время жизни объектов
5. Отладка
9. Что бы хотелось?
9
▌ Использовать эффективность асинхронного подхода
▌ Использовать простоту синхронного подхода
12. Сопрограммы
▌ Подпрограмма: полное выполнение действий
▌ Сопрограмма: возможно частичное выполнение действий
Т.е. сопрограммы обобщают подпрограммы
Примеры сопрограмм:
▌ Генераторы на языке Python.
▌ Async/await C++ proposal.
Метод сохранения контекста исполнения:
▌ Stackful сопрограммы.
12
13. Реализация сопрограмм
13
Проблема
▌ Сопрограммы всё еще не являются частью языка
▌ Необходимо применять низкоуровневые примитивы.
Решение: boost.context:
▌ Эффективное решение: использование ассемблера
▌ Десктопные платформы: Windows, Linux, MacOSX
▌ Мобильные платформы: Windows Phone, Android, IOS
▌ Процессоры: x86, x86_64, ARM, Spark, Spark64, PPC, PPC64, MIPS
▌ Компиляторы: GCC, Clang, Visual Studio
14. boost.context
14
▌ make_fcontext: создает контекст
▌ jump_fcontext: переключает контекст
Использование:
▌ 1. Аллокация памяти под стек
▌ 2. Создание контекста:
coroCtx = make_fcontext(stackPtr, stackSize, coroCb)
▌ 3. Запуск или продолжение исполнения:
jump_fcontext(callerCtx, coroCtx, opaquePtr)
▌ 4. Возвращение или заморозка исполнения:
jump_fcontext(coroCtx, callerCtx, opaquePtr)
35. Alone
35
struct Alone : IScheduler {
void schedule(Handler handler) {
strand.post(std::move(handler));
}
private:
boost::asio::io_service::strand strand;
};
strand гарантирует, что ни один обработчик не будет запущен
параллельно с другим
36. │ Alone – это
│ неблокирующая
│ синхронизация
│ без дедлоков
36
41. Теорема
41
Любая асинхронная задача может быть реализована через сопрограммы
▌ 1. Нет кода после вызова:
// код до
async(params..., cb);
// отсутствует код
// код до
synca(params...);
cb();
▌ 2. Есть код после вызова:
// код до
async(..., cb);
// код после
// код до
go { async(..., cb); };
// код после
// код до
go { synca(...); cb(); };
// код после
42. Выводы
42
▌ Синхронный подход: простота
▌ Асинхронный подход : эффективность
▌ Неблокирующая синхронизация без дедлоков
▌ Работает с исключениями
▌ Прозрачно для использования
▌ Принципиально новые подходы и паттерны
▌ Это прикольно!
https://github.com/gridem/Synca
https://bitbucket.org/gridem/synca