Fiber Scheduler 支持在 Ruby 中进行异步编程。该功能是 Ruby 3.0 的重要新增功能之一,也是令人敬畏的 async gem的核心组件之一。
最好的部分是您不需要整个框架即可开始!使用独立的 Fiber Scheduler 和几个内置的Ruby 方法就可以实现异步编程的好处。
Fiber Scheduler由两部分组成:
光纤调度器接口语言内置的一组用于阻塞操作的钩子。钩子实现被委托给Fiber.scheduler对象。光纤调度器实现实现异步行为。这是一个需要程序员明确设置的对象,因为 Ruby 不提供默认的 Fiber Scheduler 实现。
非常感谢塞缪尔·威廉姆斯!他是一名 Ruby 核心开发人员,他设计并在语言中实现了 Fiber Scheduler 功能。
Fiber Scheduler 接口是一组用于阻塞操作的钩子。它允许在发生阻塞操作时插入异步行为。这就像带有扭曲的回调:当异步回调执行时,主阻塞方法不会运行。
这些钩子记录在Fiber::SchedulerInterface 类中。这个 Ruby 特性背后的一些主要思想是:
让我们看一下如何Kernel#sleep实现钩子的示例。实际上,所有的钩子都是用 C 编码的,但为了清楚起见,这里使用了 Ruby 伪代码。
module Kernel
def sleep(duration = nil)
if Fiber.scheduler
Fiber.scheduler.kernel_sleep(duration)
else
synchronous_sleep(duration)
end
end
end上面的代码如下:
其他钩子以类似的方式工作。
“阻塞操作”这个概念已经被提到过几次了,但它的真正含义是什么?阻塞操作是 Ruby 进程(更具体地说:当前线程)最终等待的任何操作。阻塞操作的更具描述性的名称是“等待操作”。
一些例子是:
作为一个反例,以下代码段需要一段时间才能完成,但不包含阻塞操作:
def fibonacci(n)
return n if [0, 1].include? n
fibonacci(n - 1) + fibonacci(n - 2)
end
fibonacci(100)得到结果fibonacci(100)需要很多等待,但等待的只是程序员!Ruby 解释器一直在工作,在后台处理数字。一个朴素的斐波那契实现不包含阻塞操作。
对阻塞操作是(和不是)有什么直觉是值得的,因为异步编程的重点是同时等待多个阻塞操作。
该实现是 Fiber Scheduler 功能的第二大部分。
如果要在 Ruby 中启用异步行为,则需要为当前线程设置一个 Fiber Scheduler 对象。用这个Fiber.set_scheduler(scheduler)方法就完成了。该实现通常是一个定义了所有Fiber::SchedulerInterface方法的类。
Ruby 没有提供默认的 Fiber Scheduler 类,也没有提供可用于该目的的对象。这似乎不寻常,但不包括使用该语言实现的 Fiber Scheduler 实际上是一个很好的长期决定。最好将这个相对快速发展的问题留在核心 Ruby 之外。
从头开始编写 Fiber Scheduler 类是一项复杂的任务,因此最好使用现有的解决方案。可以在Fiber Scheduler List 项目中找到实现列表、它们的主要区别和建议。
让我们看看仅使用 Fiber Scheduler 有什么可能。
所有示例都使用 Ruby 3.1 和Fiber_schedulerFiberScheduler gem 中的类,它由您真正维护。这个 gem不是示例的硬依赖,因为如果将引用替换为另一个 Fiber Scheduler 类,下面的每个片段都应该仍然有效。FiberScheduler
这是一个简单的例子:
require "fiber_scheduler"
require "open-uri"
Fiber.set_scheduler(FiberScheduler.new)
Fiber.schedule do
URI.open("https://httpbin.org/delay/2")
end
Fiber.schedule do
URI.open("https://httpbin.org/delay/2")
end上面的代码创建了两个纤程,每个纤程发出一个 HTTP 请求。请求并行运行,整个程序在 2 秒内完成。
Fiber.set_scheduler(FiberScheduler.new)在当前线程中设置一个 Fiber Scheduler,使Fiber.schedule方法能够工作,并且使 Fiber 以异步方式运行。Fiber.schedule { ... }这是一个内置的Ruby 方法,可以启动新的异步纤程。
该示例仅使用标准的 Ruby 方法——这两种方法自 Ruby 3.0Fiber.set_scheduler起Fiber.schedule就已可用。
让我们看看运行多种不同的操作是什么样子的:
require "fiber_scheduler"
require "httparty"
require "open-uri"
require "redis"
require "sequel"
DB = Sequel.postgres
Sequel.extension(:fiber_concurrency)
Fiber.set_scheduler(FiberScheduler.new)
Fiber.schedule do
URI.open("https://httpbin.org/delay/2")
end
Fiber.schedule do
# Use any HTTP library
HTTParty.get("https://httpbin.org/delay/2")
end
Fiber.schedule do
# Works with any TCP protocol library
Redis.new.blpop("abc123", 2)
end
Fiber.schedule do
# Make database queries
DB.run("SELECT pg_sleep(2)")
end
Fiber.schedule do
sleep 2
end
Fiber.schedule do
# Run system commands
`sleep 2`
end如果我们按顺序运行这个程序,大约需要 12 秒才能完成。但由于操作并行运行,总运行时间仅超过 2 秒。
您不仅限于发出 HTTP 请求。任何内置在 Ruby 中或由外部 gem 实现的阻塞操作都有效!
这是一个简单但综合的示例,同时运行一万次操作。
require "fiber_scheduler"
Fiber.set_scheduler(FiberScheduler.new)
10_000.times do
Fiber.schedule do
sleep 2
end
end上面的代码在 2 秒多一点的时间内完成。
由于其低开销,该sleep方法被选为缩放示例。如果我们使用网络请求,由于建立数千个连接和执行 SSL 握手等的开销,执行时间会更长。
异步编程的主要好处之一是同时等待许多阻塞操作。随着阻塞操作数量的增加,好处也会增加。幸运的是,运行大量光纤非常容易。
Ruby 只需一个 Fiber Scheduler 和几个内置方法就可以异步工作——不需要任何框架!
让它工作很容易。选择一个Fiber Scheduler 实现,然后使用这些方法:
一旦你开始使用它,你可以通过将任何代码包装在一个Fiber.schedule块中来使其异步。
Fiber.schedule do
SynchronousCode.run
end使用这种方法可以轻松地将整个库转换为异步,并且很少需要比这里显示的更多的努力。
异步编程的一大好处是并行化阻塞/等待操作以减少程序运行时间。这通常转化为在单个 CPU 上运行更多操作,或者更好的是,使用 Web 服务器处理更多请求。
使用 Fiber Scheduler 进行愉快的编程!
| 留言与评论(共有 0 条评论) “” |