0Pricing
Ruby Academy · Урок

Объяснение GVL

Почему потоки ограничены

«Объяснение GVL» — бесплатный урок Ruby Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Ruby Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Ruby Academy содержит 4 уроков всего.

Что такое GVL

GVL (глобальная блокировка VM, исторически GIL) — это блокировка внутри CRuby, стандартного интерпретатора Ruby.

  • Она позволяет только одному потоку одновременно выполнять код Ruby.
  • Она защищает внутренние механизмы интерпретатора от повреждения при конкурентном выполнении.
  • Из-за неё вычисления только на Ruby не выполняются по-настоящему параллельно на нескольких ядрах.

Понимание GVL помогает определить, когда потоки полезны, а когда нет.

Зачем он нужен

Внутренние механизмы CRuby (выделение объектов, сборка мусора, расширения на C) не были полностью написаны с расчётом на безопасную работу в нескольких потоках. GVL делает их безопасными, последовательно выполняя код Ruby.

Корректно убрать GVL сложно, поэтому CRuby сохраняет его. В JRuby и TruffleRuby GVL отсутствует, поэтому они могут запускать потоки на нескольких ядрах.

Работа с ограничением по CPU

Для задач с ограничением по CPU (математика, сортировка, разбор данных) потоки не ускоряют работу в CRuby. В каждый момент код Ruby выполняется только в одном потоке, поэтому два вычислительных потока примерно равны одному потоку с дополнительными накладными расходами.

def heavy
  sum = 0
  1_000_00.times { |i| sum += i }
  sum
end

puts heavy
puts "CPU work does not parallelize under the GVL"

Ввод-вывод освобождает GVL

Главная идея такова: блокирующий ввод-вывод освобождает GVL. Когда поток приостанавливается, читает файл или ждёт данных из сокета, он освобождает блокировку, чтобы другой поток мог выполнять код Ruby.

Поэтому потоки отлично подходят для работы с вводом-выводом даже при наличии GVL.

Перекрывающееся ожидание

Здесь три потока выполняют паузу. Поскольку sleep освобождает GVL, периоды ожидания перекрываются. Общее время составляет примерно 0,2 с, а не 0,6 с.

start = Time.now

threads = (1..3).map do
  Thread.new { sleep 0.2 }
end
threads.each(&:join)

elapsed = Time.now - start
puts "Elapsed roughly: " + elapsed.round(1).to_s

Расширения на C могут освобождать GVL

Хорошо написанные расширения на C могут освобождать GVL во время длительных вычислений на машинном коде с помощью rb_thread_call_without_gvl. Благодаря этому тяжёлая работа на машинном коде выполняется параллельно, пока потоки Ruby продолжают работу.

Таким образом, некоторые библиотеки обеспечивают настоящее параллельное выполнение даже в CRuby.

Потоки всё равно переключаются

Даже при вычислительной работе GVL не замораживает другие потоки навсегда. CRuby периодически принудительно переключает поток, чтобы каждый получил возможность выполняться. Работа выполняется конкурентно (чередуясь), но не параллельно (одновременно).

results = []
mutex = Mutex.new

threads = (1..3).map do |n|
  Thread.new do
    mutex.synchronize { results << n }
  end
end
threads.each(&:join)

puts "Collected: " + results.sort.join(", ")

Конкурентность и параллелизм

Два важных термина:

  • Конкурентность: задачи продвигаются вперёд, чередуясь. GVL это позволяет.
  • Параллелизм: задачи выполняются в один и тот же момент на разных ядрах. GVL не позволяет делать это с кодом Ruby.

Потоки обеспечивают конкурентность, а акторы Ractor — параллелизм.

Как обойти ограничения GVL

Чтобы использовать несколько ядер для вычислительной работы в CRuby, можно:

  • Запустить несколько процессов (у каждого собственный GVL).
  • Использовать акторы Ractor для параллельного выполнения внутри одного процесса.
  • Передать работу расширениям на C, которые освобождают GVL.

Для работы с вводом-выводом обычно достаточно обычных потоков.

У процессов нет общего GVL

Каждый процесс OS запускает собственную VM Ruby со своим GVL. Разветвление процессов обеспечивает настоящий параллелизм, но процессы не используют общую память и потребляют больше ресурсов.

puts "Main pid: " + Process.pid.to_s
puts "Each forked process has its own independent GVL"

Выбор стратегии

Руководство по выбору:

  • Много ожидания? Используйте потоки.
  • Интенсивные вычисления, нужно задействовать ядра? Используйте процессы или акторы Ractor.
  • Нужно общее состояние? Используйте потоки с Mutex, но учитывайте ограничения GVL.

Понимание GVL помогает избежать классической ошибки — ожидания линейного масштабирования вычислительных потоков.

Быстрая проверка

Проверьте, насколько хорошо Вы поняли работу GVL.

Повторение: GVL

Основные выводы о глобальной блокировке VM:

  • В CRuby в каждый момент только один поток выполняет код Ruby.
  • Она защищает внутренние механизмы интерпретатора.
  • Блокирующий ввод-вывод освобождает её, позволяя перекрывать периоды ожидания.
  • Вычислительная работа на Ruby не выполняется параллельно на нескольких ядрах.
  • Для настоящего параллелизма используйте процессы или акторы Ractor.

Далее мы рассмотрим волокна для лёгкой конкурентности с явной передачей управления.

Часто задаваемые вопросы

Урок «Объяснение GVL» бесплатный?

Да — полный текст урока «Объяснение GVL» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Ruby Academy, подпишись на CoddyKit PRO. Курс Ruby Academy содержит 4 уроков всего.

Чему я научусь в уроке «Объяснение GVL»?

Почему потоки ограничены Ты практикуешь Ruby Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Ruby Academy?

Предыдущий опыт не требуется. Ruby Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Объяснение GVL»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Ruby Academy?

Да. Каждый урок Ruby Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Потоки
  2. Объяснение GVL
  3. Волокна
  4. Ractors
← Назад к Ruby Academy