Как стать автором
Обновить

Взгляд на Tokio: как устроен этот асинхронный обработчик событий

Время на прочтение7 мин
Количество просмотров12K
Всего голосов 33: ↑32 и ↓1+31
Комментарии31

Комментарии 31

Ровно, как и «один в один» Netty. Да и, наверное, любой NIO-фреймворк. Все мы пляшем вокруг poll.

Лучше бы сделали аналог do в Haskell и for в Scala. Можно было бы писать на полноценных фьючах чистый и понятный код, не засоряя его async/await.

async/await существенно удобнее for/do. Только лишь с помощью комбинаторов очень многие паттерны выражать безумно геморройно. Год назад в одном проекте на скале большие асинхронные методы сначала мы пытались писать с помощью комбинаторов и for. Получалось совершенно нечитаемое месиво. После перехода на scala-async код стал чище и понятнее на порядок.


С async-await в Rust писать асинхронный код очень легко и приятно. А с недавними разработками в области immovable generators станет все совсем хорошо.

Интересно, а если оператор? Перегрузить в качестве комбинатора? Должно сейчас же на найтли сработать вместо await

В Haskell это сделано оператором (>>=)
То есть
do
   x <- future1
   y <- genFuture2 x
   pure y

можно записать как
future1 >>= genFuture2

Но практика показывает, что do обычно читабельнее.
В Scala вместо оператора (>>=) почему то сделали метод flatMap. for вызлядит симпатичнее.
for {
  x <- future1
  y <- genFuture2(x)
} yield {
  y
}

против
future1.flatMap(genFuture2)
А теперь добавьте в код несколько циклов…
Вот в циклах с async/await я бы работать не рискнул, поскольку не очень понимаю его семантику в этом случае. Использовавшие его коллеги в этих случаях сталкивались с неожиданностями.
Я обычно использую sequence, которая меняет местами Seq и монаду (в данном случае Future).
А что не так с семантикой?

await приостанавливает выполнение асинхронной функции (освобождая поток) пока не произойдет события. Его семантика никак не меняется в цикле.
То есть не гарантируется, что он не будет удерживать нить во время ожидания?
Гарантируется.

Ну, на самом деле зависит от реализации, но реализация без таких гарантий будет ошибкой.
Тогда я не понимаю в какой код преобразуется
async {
   for( i <- Seq(1,2,3,4)) {
      val x = await { f(i) }
      println(x)
   }
}

Ну так скопмпилируйте а потом декомпилируйте обратно...


Если это Scala — то скорее всего там конечный автомат с тремя состояниями будет.

В скале это не сработает (т.к. for маппится на метод foreach, а сквозь методы async/await скальный не работает), но сработает, если заменить на цикл while. И да, это преобразуется в конечный автомат, как и в большинстве реализаций async/await (например, в Rust тоже).
То есть не гарантируется, что он не будет удерживать нить во время ожидания?

Для таких гарантий должны соблюдаться несколько правил:


  • операции должны быть неблокирующими. poll_read для fs (а иногда и для для stdin) является блокирующим вызовом.
  • должны быть зарегистрированы другие фьючи, чтобы дать возможность executor'у переключиться на другую задачу
Я работал с кодом на Scala, активно использующим async/await. Как правило, простой способ в нем разоблаться был переписать на for — код сразу получался компактнее и структурированиее.
Явное использование flatMap может выглядеть громозко и страшно, но с for такой проблемы нет.

Да нету там ничего общего, разве что epoll and kqueue

С появлением Tokio стало ясно, что используемая библиотека Mio устарела

mio не может устареть, так как автор tokio по совместительству является автором mio, под капотом tokio — обертка над mio, которая активно обновляется. Уж вы-то должны это знать.


Устарели вы, а не tokio. Мало того, что вы до сих пор сидите на tokio-core, который, вообще-то уже deprecated, у вас какое-то самописное threadpool говнище, которое жрет мои ресурсы, вы создаете кучу тредов, хотя вас об этом не просили, так у вас обработка сокетов вообще однопоточная.


Почитайте ветку, вам многое станет ясно.

Более того, stream легко может быть преобразован в future при помощи адаптера into_future.

Это слегка "заблуждение". Лучше продемонстрировать на примере futures v0.2. В новой версии авторы переименовали комбинатор .into_future() в .next(). Что сразу проясняет ситуацию.

Единственно tokio медленней на 20% чем tokio-core, а так все хорошо

Медленнее в однопоточном режиме. В многопоточной обработке событий мое приложение стало на 5% быстрее. Мы будем улучшать производительность. Закрыто много багов, улучшили архитектуру, вот что главное.

Моё многопоточное приложение стало на 20% медленнее. Если что tokio-core не ограничивает количество потоков. К тому же забавно смотреть как hyper откатывает tokio на старую версию tokio-core чтобы в бенчмарках выглядеть лучше.

Тем забавнее наблюдать за бенчмарками, ведь tokio-core сейчас является фасадом для tokio.

В этом то и прикол что hyper даунгрейдеулся на версию tokio-core="=0.1.12" в которой tokio не используется. Я сделал тоже самое, и в своих проектах сделал также. Каждому своё конечно, но как-то терять 20% не очень

Почему-то все забывают о починенных в tokio багах в погоне за скоростью.

Я что-то не уловил про баги? Какие баги? Я довольно близко знаком с tokio, не могу припомнить никаких, по крайней мере никаких фиксов в комит логе не вижу. если для вас потеря 20% это нормальн для не меня это не приемлемо.

Вот фикс бага. Без него нет возможности возвращать ошибку из UdpCodec::encode, соответственно единственный способ обработать нестандартную ситуацию — упасть. В дальнейшем этот фикс позволил отказаться от разделения кодеков tcp и udp, используя одни и те же типажи: Encoder и Decoder.


Я помню этот фикс, потому что он был сделан мной.


Я довольно близко знаком с tokio

Вы спорите с контрибьютером с вкладом в 3к строк кода и 11 закрытых PR. Но у вас-то наверняка больше)))

Сам mio то нет, но мы использовали устаревшую версию 0.5 и встала необходимость либо переписывать все под 0.6 либо сразу попробовать мигрировать на более высокоуровневый tokio.


По поводу остального думаю будет конструктивнее, если вы опишете свое видение проблемы в виде Github issue.

По поводу остального думаю будет конструктивнее, если вы опишете свое видение проблемы в виде Github issue.

Может вы еще хотите, чтобы я в вашу репу коммитил? Ваши HR пытались меня к вам заманить, собеседования всякие устраивали, но когда они 2 раза перенесли техническое, это был эпичный провал.


Так что спасибо, но нет))

Зарегистрируйтесь на Хабре, чтобы оставить комментарий