Потоки
Параллельное выполнение
«Потоки» — бесплатный урок 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 — локальная установка не требуется.