Concurrency in modern Ruby
Параллелизм в Ruby развивался с течением времени, особенно с появлением Ruby 3.0 и более поздних версий. Ниже приведены примеры того, как работает параллелизм в последних версиях Ruby.
Threads: Ruby имеет встроенную поддержку потоков. Потоки позволяют выполнять несколько задач одновременно в рамках одного процесса. Однако в более ранних версиях Ruby, до 1.9, потоки реализовывались с помощью глобальной блокировки интерпретатора (GIL), что означало, что одновременно мог выполняться только один поток кода Ruby, что делало невозможным истинное параллельное выполнение. В последних версиях Ruby (>3.0) GIL по-прежнему существует, но он был оптимизирован, чтобы уменьшить его влияние на параллельную обработку.
Fibers: в Ruby также есть концепция волокон — легковесных примитивов параллелизма. Они позволяют приостанавливать и возобновлять выполнение в определённых точках кода. В отличие от потоков, волокна не планируются операционной системой и не выполняются параллельно. Однако они полезны для реализации кооперативной многозадачности и могут использоваться для эффективного управления задачами, связанными с вводом-выводом.
Ractors: В Ruby 3 появилась модель параллелизма Ractors (ранее известная как «Guilds/Гильдии»). Ractors позволяют запускать несколько независимых единиц выполнения параллельно, с истинным параллелизмом, без влияния GIL. У каждого Ractor есть собственный контекст выполнения, и он может взаимодействовать с другими Ractor посредством передачи сообщений.
Улучшенный параллелизм: в новых версиях Ruby также улучшен параллелизм. Глобальная блокировка виртуальной машины (GVL) стала менее строгой, что позволяет эффективнее использовать несколько ядер. Это означает, что некоторые задачи, связанные с загрузкой процессора, могут выполняться параллельно, даже несмотря на наличие GIL.
Библиотеки для параллельности: Существует также несколько известных библиотек для параллельного программирования, таких как Concurrent Ruby и Celluloid, которые предлагают высокоуровневые абстракции для параллельного программирования. Эти библиотеки часто предоставляют такие функции, как акторы, фьючерсы, промисы и различные примитивы синхронизации, которые упрощают и делают более надёжным параллельное программирование.
Async/await: ещё одно важное нововведение в Ruby 3 - поддержка синтаксиса async/await, который позволяет писать асинхронный код, выглядящий синхронно, что упрощает работу с асинхронными операциями, такими как ввод-вывод, без блокировки потоков.
У Ruby зрелая экосистема с множеством простых в использовании библиотек и фреймворков для параллельного программирования, которые предоставляют высокоуровневые абстракции для параллельного программирования. Однако следует помнить, что некоторые функции параллельного программирования могут быть ограничены GIL, а управление общим состоянием между потоками может быть затруднено из-за возможных состояний гонки.