Fiber(нить) вводят в ступор любого, кто встречается с ними в первый раз. Однако сложного в них почти ничего нет, если их правильно представить. Для начала, я называю их функциями с остановкой, это должно дать некоторую идею. Но давайте лучше рассмотрим на примерах, а именно создадим одну нить, и несколько функций, параллельный вызов который даст тот же результат.
#Секция функций
def func1()
p :func1
end
def func2()
p :func2
end
def func3()
p :func3
end
p :break
func1
p :break
func2
p :break
func3
p :end_funcs
#Секция нитей
$fiber = Fiber.new{
p :fiber1
Fiber.yield
p :fiber2
Fiber.yield
p :fiber3
}
3.times{
p :break
$fiber.resume
}
p :end_fibers
Итак, мы вызвали три разные функции, и вызвали одну нить трижды, и получили эквивалентный результат. Конечно, это легко выполняется, только если порядок фиксированный, но я стараюсь просто передать вам всю суть идеи работы.
Вот еще код, позволяющий посмотреть на файбер, как на более полноценный объект, чем функция.
#функция - таймер
def timer
$timer = 0 unless $timer
$timer += 1
end
5.times{
p timer
}
#нити - таймеры
$timers = []
3.times{
$timers.push(Fiber.new{
timer = 0
while true do
timer += 1
Fiber.yield timer
end
})
}
5.times{
$timers.each{|timer|
p timer.resume
}
}
Здесь у нас одна функция – таймер, которая при каждом вызове возвращает значение, на единицу большее, чем при последнем вызове. Для этого нам требуется глобальная переменная.
Для нитей же не требуются глобальные переменные, т.к. каждая их них обладает своим пространством видимости,
сохраняющимся от вызова к вызову, что позволяет сделать множество независимых таймеров. Так же мы видим, как нить возвращает значение, так же, как и функция.
Можно передать значение и в нить, так же, как и для функции.
#функция - таймер
def timer(dt)
$timer = 0 unless $timer
$timer += dt
end
5.times{|t|
p timer(t)
}
#Нити - таймеры
$timers = []
3.times{
$timers.push(Fiber.new{|dt|
timer = 0
while true do
timer += dt
dt = Fiber.yield timer
end
})
}
5.times{|t|
$timers.each{|timer|
p timer.resume(t)
}
}
Ну и на последок – ранее поведение нити было довольно цикличным, но можно задать ему более жесткий порядок не используя глобальных переменных.
$fiber = Fiber.new{|time|
while time < 10 do
dt = Fiber.yield time
time += dt
end
while time > 0 do
dt = Fiber.yield time
time -= dt
end
while time > -100 do
Fiber.yield time
time *= dt
end
Fiber.yield time
Fiber.yield :end
}
result = 0
ia = 1
while result != :end do
result = $fiber.resume ia
ia += 1
p result
end
Здесь нить проходит целых три фазы, смена которых зависит от условий (причем безвозвратных).
Ну и, наконец, небольшая демка, которая была сделана в разгаре моих попыток объяснить преимущества нитей, смотреть на свой страх и риск.
Демо (1.3 МБ VX Ace)
P.S. Важно, если вы ничего не поняли (но довольно хорошо знакомы с Руби), то я полностью перепишу статью. Боюсь, что именно объяснение нитей мне дается с трудом.