Goodbye program!
Но подождите! Что случилось с сообщением в fiber, которое мы создали? Ответ можно найти в начале главы, в разделе "Определение fiber". Ключевые слова появятся
Это важная деталь для понимания того, как работает параллелизм в Crystal, а также того, почему природа IO, рассмотренная в
• Метод sleep
• Fiber.yield
• Операции, связанные с IO, такие как чтение/запись в файл или сокет
• Ожидание получения значения из канала
• Ожидание отправки значения в канал
• Когда текущее волокно завершит выполнение
Все эти параметры блокируют волокно, в результате чего другие волокна получают возможность выполниться. Например, добавьте sleep 1
Hello from fiber!
действительно печатается. Метод sleep
сообщает планировщику, что он должен продолжить выполнение основного волокна через одну секунду. Тем временем он может свободно выполнить следующее волокно в очереди, которое в данном случае печатает наше сообщение.Метод Fiber.yield
sleep 0
, даст тот же результат, но означает немного другое. При использовании метода sleep
с целочисленным аргументом планировщик знает, что он должен вернуться к этому волокну в какой-то момент в будущем после того, как он достаточно отоспался. Однако использование Fiber.yield
или sleep 0
позволит проверить, есть ли волокна, ожидающие выполнения, и если да, выполнить их. В противном случае это будет продолжаться без переключения. Такое поведение наиболее распространено, когда вы выполняете некоторую логику в узком цикле, но все же хотите дать возможность другим волокнам выполниться. Однако Fiber.yield
просто сообщает планировщику, что В обоих случаях единственная причина, по которой выполнение вообще переключается обратно на основное волокно, заключается в том, что что-то внутри волокна выполняет одно из действий, которые могут вызвать выполнение другого волокна. Если бы вы удалили путы и волокно состояло бы только из бесконечного цикла, это заблокировало бы волокно навсегда, и программа никогда бы не завершила работу. Если вы хотите разрешить выполнение других файберов и навсегда заблокировать основной файбер, вы можете использовать sleep
Продолжая предыдущий пример, вы можете захотеть использовать переменные внутри волокна, которые были определены за его пределами. Однако это плохая идея, поскольку она приводит к неожиданным результатам:
idx = 0
while idx < 4
spawn do
puts idx
end
idx += 1
end
Fiber.yield
Вы могли бы ожидать, что предыдущий код напечатает числа от одного до четырех, но на самом деле он печатает число четыре четыре раза. Причина этого двоякая:
• Волокна не выполняются немедленно.
• Каждое волокно ссылается на одну и ту же переменную.
Поскольку волокна не выполняются немедленно, они создаются при каждой итерации цикла while loop
idx
достигает четырех и выходит из цикла while loop
. Затем, поскольку каждое волокно ссылается на одну и ту же переменную, все они печатают текущее значение этой переменной, равное 4
. Эту проблему можно решить, переместив порождение каждого волокна в отдельный процесс, который создаст замыкание, фиксирующее значение переменная на каждой итерации. Однако это далеко не идеально, поскольку в этом нет необходимости и ухудшается читаемость кода. Лучший способ справиться с этим — использовать альтернативную форму spawn
, которая принимает вызов в качестве аргумента:idx = 0
while idx < 4
spawn puts idx
idx += 1
end
Fiber.yield