Blog/content/posts/2022/crystal/scheduled-func.md

115 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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