2022-10-08 00:33:07 +03:00
|
|
|
|
---
|
2022-11-19 03:36:06 +03:00
|
|
|
|
title: "📋 Параллелизм или расписание в Crystal"
|
2022-10-08 00:33:07 +03:00
|
|
|
|
date: 2022-10-07T22:43:29+03:00
|
|
|
|
|
draft: false
|
2022-12-28 00:46:21 +03:00
|
|
|
|
tags: [crystal, development, tutorial]
|
2022-10-08 00:33:07 +03:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Выполнять код раз в 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 памяти.
|