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