= Паралеллизм на акторах Надо начать издалека, чтобы было понятно == Режимы I/O === Нода - вычисления блокируют всех остальных - IO через async/await - блокирующий IO и долгие вычисления - моветон === C# - вычисления блокируют только наш поток - IO через async-await (выглядит как блокирующий, но надо не забывать расставлять async-await) - блокирующий IO моветон, async-await моветон, долгие вычисления - полумоветон (достаточно не запускать слишком много потоков с долгими вычислениями или запускать их в явных потоках) === Akka - вычисления блокируют только наш поток - IO через сообщения (выглядит как блокирующий) - настоящий блокирующий IO моветон, долгие вычисления - полумоветон == "Внутри" и "снаружи" == Нода Снаружи * пул * мультиплексор Внутри * вычисления * async-await (везде, где есть async-await, также есть коллбеки и промисы) == C# Cнаружи * явные потоки * пул * мультиплексор Внутри * явные потоки (если очень надо) * пул (если надо) * async-await == Akka Снаружи: * явные потоки * пул * мультиплексор * async-await Внутри: * вычисления * отправка сообщений == Структура базового варианта приложения на Akka К сожалению, нет библиотек, рассчитанных на акку. То есть нам понадобится IO-библиотека, оборачивающая весь IO в акторы. Благо, в коллекторе IO немного - HTTP и веб-сокеты. Зато мы получаем очень ясный код для каждой биржи, а все неясности будут вынесены в наши модули: * akka.http * akka.gearman * akka.websocket Эти модули мы можем заопенсорсить и зарелизить в !NuGet. Таким образом, коллектор состоит из * "библиотечной" части, использующей "наружные" примитивы: * явные потоки * пул * async-await * собственно кода бирж, содержащего только "внутренние" примитивы: * вычисления * синхронные вызовы == Отказоустойчивость После реализации базовых функций необходимо реализовать средства повышения отказоустойчивости. Для этого будет реализован компонент на супервизорном API акки, занимающийся пуском-остановкой-сохранением состояния акторов коллектора. {{{#!comment === Нода - 1 поток - долгие вычисления надо разбивать на куски, чтобы не блокировать единственный поток - IO через мультиплексор: - коллбеки - промисы - async-await - блокирующий IO на крайняк == C# - от 2 до 10 потоков на ядро ("пул") - длинные вычисления надо запускать в небольшом количестве инстансов, чтобы не блокировать пул - IO через мультиплексор: - async-await - === DOS PIO {{{ foo = read() }}} во время `read()` наш основной поток никуда не девается, и занимается вводом-выводом сам (читает из регистров через инструкцию `IN` в цикле) === DOS DMA {{{ read() while (!isCompleted()) { doSomethingElse(); } foo = getResult() }}} Тут сам ввод происходит аппаратно, мы только запускаем его и опрашиваем факт завершения. Идея в том, что если мы не опрашиваем флаг завершения - то ввод всё равно завершается удачно. В случае DOS DMA нам надо читать постоянно, дабы не потерять байты. А тут мы можем заниматься какой-то работой, пока идет ввод-вывод, и опрашивать "редко". == UNIX Blocking IO Идея та же, что в DOS PIO: {{{ foo = read() }}} Только теперь когда мы делаем read() мы даем команду ядру нас остановить и продолжить, когда придёт ответ. В принципе идентично `yield` из JS. Отличие от DOS PIO в том, что пока мы остановлены, ядро может исполнять кого-то другого. == UNIX nonblocking IO {{{ read() while (!isCompleted()) { doSomethingElse(); } foo = getResult() }}} Те же яйца что в случае DOS DMA. Только теперь аппаратный ввод не обязателен а им может заниматься ядро в соседнем потоке. }}}