Ruby предлагает несколько подходов для достижения параллельности, несмотря на ограничения GIL. В этой статье разберёмся, как использовать процессы через fork, асинхронные библиотеки и новые возможности вроде Ractor, а также когда стоит перейти на альтернативные реализации Ruby вроде JRuby.
В прошлой статье мы выяснили, что потоки в MRI Ruby — это вежливый обман.
А что делать, если нужна настоящая параллельность? Использовать процессы, асинхронщину, и альтернативные VM. Сегодня поговорим о:
fork- гемах типа
Parallel Ractor(в Ruby 3+)- и немного
async,httpx,falcon
🧠 fork — создаём процессы
pid = fork do
puts "Я дочерний процесс, мой PID #{Process.pid}"
sleep 1
end
puts "Я родитель, PID #{Process.pid}"
Process.wait(pid)
forkсоздаёт новый процесс, копию текущего.- В отличие от потоков — это настоящая параллельность (на уровне ОС).
- Работает только в Unix-системах (не Windows).
⚙️ Parallel — гем, который абстрагирует fork
require 'parallel'
results = Parallel.map([1, 2, 3, 4], in_processes: 4) do |i|
i * i
end
puts results.inspect # => [1, 4, 9, 16]
Parallel.mapзапускает код вfork-процессах.- Есть
in_threads, ноin_processes— без GIL.
🧬 Ractor (Ruby 3+)
r = Ractor.new do
val = Ractor.receive
Ractor.yield val * 2
end
r.send(21)
puts r.take # => 42
- Ractor — новый механизм параллельности в Ruby 3.
- Каждый
Ractorимеет свой изолированный объектный мир. - Без GIL — параллельное выполнение Ruby-кода возможно.
- Минус: жёсткие ограничения (объекты должны быть “shareable”).
🌊 async — асинхронность без потоков
require 'async'
require 'net/http'
Async do
3.times do
Async do
puts Net::HTTP.get(URI("https://example.com"))
end
end
end
- Библиотека
async - Реализует кооперативную многозадачность, как
async/awaitв JS. - Не блокирует потоки, пока ждёт I/O.
🚀 Falcon и HTTPX
Идеальны для высоконагруженных I/O приложений — чат-ботов, стриминга, пушей и т.д.
🤖 А если нужен реально многопоточный Ruby?
- JRuby — Ruby на JVM: потоки = настоящие Java-потоки, GIL нет.
- TruffleRuby — часть GraalVM, потенциально поддерживает конкурентность.
- Rubinius — попытка сделать Ruby без GIL (уже не развивается активно).
🧨 Что могут спросить на собесе?
“Как ты реализуешь CPU-bound парралельную обработку в MRI?”
Ответ:
- fork через
Parallel(если Unix) - Или использовать JRuby/TruffleRuby
- Ractor — в теории, но ещё сырой
🔚 Вывод: MRI Ruby не про параллельность, но обходные пути есть. Важно знать, что когда применять:
| Задача | Что использовать |
|---|---|
| I/O-bound | Thread, async, httpx |
| CPU-bound (в MRI) | fork, Parallel |
| CPU-bound (без MRI) | JRuby, TruffleRuby |
| Хардкор-изоляция | Ractor |