---
title: "📋 Параллелизм или расписание в Crystal"
date: 2022-10-07T22:43:29+03:00
draft: false
tags: [crystal, development, 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 памяти.
