0Pricing
Ruby Academy · Урок

Потоки

Параллельное выполнение

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

Что такое поток

Поток — это независимый путь выполнения внутри программы. Ruby позволяет запускать несколько потоков, чтобы их работа выполнялась одновременно.

  • Основная программа выполняется в основном потоке.
  • Дополнительные потоки создаются с помощью Thread.new.
  • Потоки используют одну и ту же память, поэтому могут читать и изменять одни и те же переменные.

Потоки особенно полезны, когда программа проводит время в состоянии ожидания (сеть, диск, пауза): один поток может ждать, пока другой продолжает работу.

Создание потока

Передайте блок в Thread.new. Блок выполняется в новом потоке. Вызовите join, чтобы дождаться его завершения до выхода программы.

t = Thread.new do
  puts "Hello from the new thread"
end

t.join
puts "Main thread done"

Почему важен join

Когда основной поток завершается, Ruby завершает работу и может не дожидаться других потоков. join блокирует вызывающий поток, пока целевой поток не завершится.

Без join рабочий поток может не успеть вывести сообщение до завершения программы.

t = Thread.new do
  sleep 0.1
  puts "Worker finished"
end

puts "Waiting..."
t.join
puts "All done"

Несколько потоков одновременно

Можно запустить группу потоков, собрать их в массив, а затем вызвать join для каждого. Это распространённый шаблон для работы с вводом-выводом, похожей на параллельную.

threads = (1..3).map do |n|
  Thread.new do
    puts "Thread number " + n.to_s
  end
end

threads.each(&:join)
puts "Finished all threads"

Совместное использование переменных

Потоки используют общую память. Переменная, определённая до запуска потока, видна внутри него. Это удобно, но опасно, если два потока изменяют одно и то же значение.

counter = 0

t = Thread.new do
  counter += 1
end

t.join
puts "Counter is " + counter.to_s

Состояния гонки

Состояние гонки возникает, когда потоки чередуют изменения общих данных, а результат зависит от времени выполнения.

  • counter += 1 на самом деле состоит из чтения, сложения и записи.
  • Два потока могут прочитать одно и то же старое значение и записать одно и то же новое, из-за чего одно увеличение потеряется.

Решение — согласовать доступ с помощью блокировки.

Мьютекс: блокировка

Mutex (взаимное исключение) гарантирует, что защищённый код в каждый момент выполняется только одним потоком. Оберните критическую секцию в mutex.synchronize.

mutex = Mutex.new
counter = 0

threads = (1..5).map do
  Thread.new do
    mutex.synchronize do
      counter += 1
    end
  end
end

threads.each(&:join)
puts "Counter is " + counter.to_s

Возврат значения

Thread#value дожидается завершения потока и возвращает значение его блока — это сочетание автоматического join и результата.

t = Thread.new do
  2 + 3
end

puts "Result is " + t.value.to_s

Локальные переменные потока

Каждый поток может хранить собственные значения, обращаясь к ним по ключу-символу через Thread.current[:key]. Эти значения принадлежат только данному потоку и никогда не становятся общими.

t = Thread.new do
  Thread.current[:name] = "worker"
  puts "Inside: " + Thread.current[:name]
end

t.join

Обработка исключений

Если поток выдаёт ошибку, она остаётся незаметной, пока Вы не вызовете join или value, после чего ошибка возникнет повторно. Установка Thread.abort_on_exception = true немедленно завершает всю программу при ошибке, что помогает при отладке.

t = Thread.new do
  raise "boom"
end

begin
  t.join
rescue => e
  puts "Caught: " + e.message
end

Когда потоки полезны

Потоки особенно эффективны для работы с ограничением по вводу-выводу, когда время уходит на ожидание. Для работы с ограничением по CPU они полезны меньше из-за GVL (об этом далее).

  • Хороший вариант: загрузка множества URL-адресов, чтение множества файлов.
  • Ограниченная польза: интенсивные числовые вычисления на нескольких ядрах.

Всегда защищайте общее изменяемое состояние с помощью Mutex.

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

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

Повторение: потоки

Вы изучили основные сведения о потоках Ruby:

  • Thread.new запускает конкурентное выполнение.
  • join дожидается завершения; value дожидается завершения и возвращает результат.
  • Потоки используют общую память, что приводит к состояниям гонки.
  • Mutex с synchronize защищает общее состояние.
  • Потоки особенно эффективны для работы с вводом-выводом.

Далее мы разберём, почему потоки не могут полностью использовать несколько ядер CPU: GVL.

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

Урок «Потоки» бесплатный?

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

Чему я научусь в уроке «Потоки»?

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

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

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

Сколько времени занимает урок «Потоки»?

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

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

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

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

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