--- title: "Параллелизм или расписание в Crystal" date: 2022-10-07T22:43:29+03:00 draft: false tags: [crystal, develop, tutorial] --- ## Выполнять код раз в 10 секунд? Обычная задача в буднях говнокодера. Про параллелизм в [Crystal](https://crystal-lang.org/) есть хорошая статья [Concurrency vs. Parallelism](https://crystal-lang.org/reference/1.6/guides/concurrency.html). В целом если её прочитать, то необходимость в прочтении этого руководства отпадает полностью. Ничего нового ты здесь не узнаешь, однако я продолжу... ## Глубокий сон [sleep](https://crystal-lang.org/api/1.6.0/toplevel.html#sleep%3ANil-class-method) — Top Level метод, который блокирует текущий `fiber`\* \* [Fiber](https://crystal-lang.org/api/1.6.0/Fiber.html) — это исполняемый блок кода, управляемый райнтаймом Crystal. Концептуально Fiber похож на поток (thread), но с меньшими затратами и полностью внутренне связан с процессом Crystal. Рантайм включает планировщик, который очевидно планирует выполнение волокон. Связь между волокнами обычно осуществляется через [Channel](https://crystal-lang.org/api/1.6.0/Channel.html). ## Цикличное выполнение Для реализации цикла в Crystal можно использовать оператор `while`: ```crystal while p! "╭∩╮(Ο_Ο)╭∩╮".class end ``` Или даже оператор `loop`: ```crystal loop do p! "╭∩╮(Ο_Ο)╭∩╮".class end ``` А если запихонить в цикл метод `sleep`, то получим уже имитацию выполнения блока кода по расписанию: ```crystal loop do p! "╭∩╮(Ο_Ο)╭∩╮".class sleep 2 end ``` Но ты же наблюдательный и понял, что пока один loop/while не закончит выполнение, второй цикл не запустится. Вот для таких случаем и нужны Fiber или волокна, просто меня, но я буду называть их `Файберами`. ## Файберы Попробуй запустить этот пример и посмотри что получится. ```crystal spawn do loop do p! "╭∩╮(Ο_Ο)╭∩╮".class end end spawn do loop do puts Time.local.to_unix end end sleep ``` Тут я создал два файбера, в которых циклично выполняется некий код, а главный процесс программы я зациклил методом `sleep`. Удивительно, не правда ли?) Запихониваем в циклы `sleep ` и получаем выполнение каждого файбера с разным интервалом. ## wrapper Всё эту простыню можно оформить таким образом: ```crystal def every(period : Time::Span, &block : -> T) forall T spawn do loop do block.call sleep period end end end every(2.seconds) { puts "-@-@-" } every(4.seconds) { puts "(-.-)Zzz..." } sleep ``` Жрёт это всё ~2Mb памяти.