! В Ruby используется Global Locking вместо реальных потоков, так что особо бояться состояния гонки не стоит !
В век компьютеров с несколькими ядрами на одном кристале глупо забывать о таком прекрасно способе оптимизации, как многопоточность. Суть данной возможности проста - при наличии больше, чем одного ядра можно разделить сложную задачу на несколько маленьких, которые с помощью шедулера ОС будут аккуратно намазаны на несколько ядер, нежели один поток, который будет постоянно крутиться на одном ядре.
Здесь всё просто. Даже не надо подключать никакие модули. Берём класс Thread, даём ему блок и voila - мы создали поток.
threads = [] threads << Thread.new do puts 'Thread 1 started' sleep 2 puts 'Thread 1 finished' end threads << Thread.new do puts 'Thread 2 started' sleep 1 puts 'Thread 2 finished' end while threads.length > 0 threads.delete_if do |t| unless t.alive? t.join true end end end
Если не добавить в конце цикл, отслеживающий состояние потоков и правильно их не завершающий - родительский поток закончит своё выполнение и завершится, прервав все дочерние потоки.
Каждый, кто хоть раз занимался многопоточным программированием, сталкивался с состоянием гонки - процессом, когда два или более потока в рандомное время обращаются к одному и тому же ресурсу. Это может привести к тому, что два потока обратятся к одному счётчику одновременно и вместо того, чтобы он увеличился на 2, он увеличится на 1, поскольку каждый поток запишет своё значение. Для того, чтобы состояние гонки не возникало - при работе с общими ресурсами применяют мьютексы (блокировки). В ruby они сделаны максимально прозрачно (не надо думать о том, залочил ты мьютекс, разлочил, всё ли почистил)
class Sheep def initialize @shorn = false end def shorn? @shorn end def shear! print "shearing... " @shorn = true end end sheep = Sheep.new 5.times.map do Thread.new do unless sheep.shorn? sheep.shear! end end end.each(&:join)
Если позапускать данный код несколько раз - можно увидеть, что овечку иногда бреют аж до пяти раз, хотя по идее после первого раза переменная должна быть установлена в нужные значения. После добавления мьютекса всё становится хорошо:
class Sheep def initialize @shorn = false @mutex = Mutex.new end def shorn? @shorn end def shear! print "shearing... " @shorn = true end def mutex @mutex end end sheep = Sheep.new 5.times.map do Thread.new do sheep.mutex.synchronize do unless sheep.shorn? sheep.shear! end end end end.each(&:join) puts ""