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

Например, программа, предназначенная для одновременной отправки электронной почты тысячам людей, может разделить задачу между 20 различными потоками, которые отправляют электронную почту одновременно. Такой параллелизм быстрее, чем обработка одного элемента за другим, особенно в системах с более чем одним процессором, поскольку разные потоки выполнения могут выполняться на разных процессорах. Это также может быть быстрее, потому что вместо того, чтобы тратить время на ожидание ответа от удаленного компьютера, вы можете продолжить другие операции.

threads = []
10.times do
  thread = Thread.new do
    10.times { |i| print i; $stdout.flush; sleep rand(2) }
  end
  threads << thread
end
threads.each { |thread| thread.join }

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

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

После того, как вы создали потоки, вы ждете, пока все они завершатся, прежде чем программа завершит работу. Вы ждете, перебирая все объекты потоков в потоках и вызывая метод соединения каждого потока. Метод соединения заставляет основную программу ждать завершения выполнения потока, прежде чем продолжить. Таким образом, вы убедитесь, что все потоки завершены перед выходом.

Предыдущая программа приводит к выводу, подобному следующему (вариация обусловлена ​​​​случайностью спящего режима):

Вывод: 00101200010010101212312124232512323453234336634544365546744548776557886689756765679797898788898999999

В примере создано десять потоков Ruby, единственной задачей которых является подсчет и случайный сон. Это приводит к предыдущему псевдослучайному выводу.

Расширенные операции с потоками

1. Ожидание завершения потоков Redux

Когда вы ждали завершения ваших потоков с помощью метода соединения, вы могли бы указать значение тайм-аута (в секундах), для которого нужно ждать. Если поток не завершается в течение этого времени, join возвращает nil. Вот пример, где каждому потоку дается только одна секунда на выполнение:

threads.each do |thread|
  puts "Thread #{thread.object_id} didn't finish in 1s" unless thread.join(1)
end

2. Получение списка всех потоков

С помощью Thread.list можно получить глобальный список всех потоков, запущенных в вашей программе.

10.times { Thread.new { 10.times { |i| print i; $stdout.flush; sleep rand(2) } } } Thread.list.each { |thread| thread.join unless thread == Thread.main }

3. Операции с потоками из самих потоков

Потоки — это не просто крошечные глупые фрагменты кода. У них есть возможность общаться с планировщиком потоков Ruby и сообщать об обновлении своего статуса. Например, поток может остановить сам себя:

Thread.new do
  10.times do |i|
    print i
    Thread.stop
  end
end

Каждый раз, когда поток, созданный в этом примере, выводит число на экран, он останавливается. Затем он может быть перезапущен или возобновлен только родительской программой, вызвавшей метод run в потоке, например:

Thread.list.each { |thread| thread.run }

Надеюсь, вы поймете концепцию потоков в ruby. Спасибо:)