据我所知,水晶循环光纤与 io,这意味着如果一根光纤正在等待 io,水晶将切换到另一根光纤。
如果我们生成了两根纤程,但其中一根在没有 io 的情况下进行持续计算/循环怎么办?
例如,使用下面的代码,服务器不响应任何 http 请求
spawn do
Kemal.run
end
spawn do
# constant computation/loop with no IO
some_func
end
Fiber.yield
# or sleep
据我所知,水晶循环光纤与 io,这意味着如果一根光纤正在等待 io,水晶将切换到另一根光纤。
如果我们生成了两根纤程,但其中一根在没有 io 的情况下进行持续计算/循环怎么办?
例如,使用下面的代码,服务器不响应任何 http 请求
spawn do
Kemal.run
end
spawn do
# constant computation/loop with no IO
some_func
end
Fiber.yield
# or sleep
默认情况下,Crystal 使用协作式多任务处理。为了实现它,Crystal 运行时提供了Fibers。由于它们的合作性质,您必须不时让出执行(例如使用Fibers.yield):
纤维是合作的。这意味着只有在提供光纤时才能从光纤中提取执行。它的执行不能被随机中断。为了使并发工作,纤程必须确保偶尔为调度程序提供挂钩以交换其他纤程。[...]
当计算密集型任务没有或只有很少的 IO 操作时,纤程应该明确地提供不时使用 Fiber.yield 来中断紧密循环的执行。此调用的频率取决于应用程序和并发模型。
请注意,CPU 密集型操作并不是导致其他 Fiber 饥饿的唯一来源。当调用可能阻塞的 C 库时,Fiber 也会等待操作完成。一个例子是长轮询操作,它将等待下一个事件或最终超时(例如Kafka 中的rd_kafka_poll)。为防止这种情况,请选择异步 API 版本(如果可用),或使用较短的轮询间隔(例如 0 用于 Kafka 轮询)并将睡眠操作转移到 Crystal 运行时,以便其他 Fiber 可以运行。
2019 年,Crystal 引入了对并行性的支持。通过运行多个工作线程,您还可以防止一项昂贵的计算导致所有其他操作饥饿。但是,您必须小心,因为程序的响应性(甚至可能是正确性)可能取决于工作人员的数量(例如,只有一名工作人员,它仍然会挂起)。总体而言,在时间广泛的操作中偶尔让步似乎是更好的解决方案,即使您最终使用多个工作人员来提高多核机器上的性能。