Blog/content/posts/2022/crystal/scheduled-func.md
2022-11-19 03:36:06 +03:00

3.6 KiB
Raw Blame History

title date draft tags
📋 Параллелизм или расписание в Crystal 2022-10-07T22:43:29+03:00 false
crystal
develop
tutorial

Выполнять код раз в 10 секунд?

Обычная задача в буднях говнокодера.

Про параллелизм в Crystal есть хорошая статья Concurrency vs. Parallelism.

В целом если её прочитать, то необходимость в прочтении этого руководства отпадает полностью.
Ничего нового ты здесь не узнаешь, однако я продолжу...

Глубокий сон

sleep — Top Level метод, который блокирует текущий fiber*

* Fiber — это исполняемый блок кода, управляемый райнтаймом Crystal.
Концептуально Fiber похож на поток (thread), но с меньшими затратами и полностью внутренне связан с процессом Crystal.
Рантайм включает планировщик, который очевидно планирует выполнение волокон.

Связь между волокнами обычно осуществляется через Channel.

Цикличное выполнение

Для реализации цикла в Crystal можно использовать оператор while:

while p! "╭∩╮(Ο_Ο)╭∩╮".class
end

Или даже оператор loop:

loop do
  p! "╭∩╮(Ο_Ο)╭∩╮".class
end

А если запихонить в цикл метод sleep, то получим уже имитацию выполнения блока кода по расписанию:

loop do
  p! "╭∩╮(Ο_Ο)╭∩╮".class

  sleep 2
end

Но ты же наблюдательный и понял, что пока один loop/while не закончит выполнение, второй цикл не запустится.

Вот для таких случаем и нужны Fiber или волокна, просто меня, но я буду называть их Файберами.

Файберы

Попробуй запустить этот пример и посмотри что получится.

spawn do
  loop do
    p! "╭∩╮(Ο_Ο)╭∩╮".class
  end
end

spawn do
  loop do
    puts Time.local.to_unix
  end
end

sleep

Тут я создал два файбера, в которых циклично выполняется некий код, а главный процесс программы я зациклил методом sleep.

Удивительно, не правда ли?)

Запихониваем в циклы sleep и получаем выполнение каждого файбера с разным интервалом.

wrapper

Всё эту простыню можно оформить таким образом:

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 памяти.