假设您的问题是缓慢的外部 API,解决方案可能是使用线程编程或异步编程。默认情况下,当做 IO 时,你的代码会阻塞。这基本上意味着,如果您有一个执行 HTTP 请求以检索一些 JSON 的方法,您的方法将告诉您的操作系统您将要睡觉,并且您不希望在操作系统响应之前被唤醒那个请求。由于这可能需要几秒钟,您的应用程序将不得不等待。
这种行为不仅仅针对 HTTP 请求。从文件或网络摄像头等设备中读取具有相同的含义。软件这样做是为了防止在明显没有使用 CPU 时占用 CPU。
所以你的问题是:我们真的必须等待一种方法完成才能调用另一种方法吗?如果 的行为method_two
取决于 的结果method_one
,则可以。但是在您的情况下,它们似乎是没有相互依赖的独立工作单元。因此存在并发执行的潜力。
您可以通过使用包含您要运行的代码的块初始化 Thread 类的实例来启动新线程。将线程视为程序中的程序。您的 Ruby 解释器将自动在线程和主程序之间切换。您可以启动任意数量的线程,但是您创建的线程越多,您的主程序在返回执行之前必须等待的时间越长。但是,我们可能在谈论微秒或更短的时间。让我们看一个线程执行的例子。
def main_method
Thread.new { method_one }
Thread.new { method_two }
Thread.new { method_three }
end
def method_one
# something_slow_that_does_an_http_request
end
def method_two
# something_slow_that_does_an_http_request
end
def method_three
# something_slow_that_does_an_http_request
end
调用main_method
将导致所有三个方法以看似并行的方式执行。实际上,它们仍在按顺序处理,但不是在阻塞时进入睡眠状态,而是在操作系统准备好输入时method_one
返回主线程并切换回线程。method_one
假设每个方法执行两个 2 ms 减去等待响应,这意味着所有三个方法都在 6 ms 后运行 - 几乎是立即运行。
如果我们假设响应需要 500 毫秒才能完成,这意味着您可以将总执行时间从 2 + 500 + 2 + 500 + 2 + 500 减少到 2 + 2 + 2 + 500 - 换句话说,从 1506 毫秒仅 506 毫秒。
感觉就像这些方法在同时运行,但实际上它们只是在同时休眠。
但是,在您的情况下,您面临挑战,因为您的操作依赖于一组先前操作的完成。换句话说,如果你有任务A、B、C、D、E和F,那么A、B、C、D和E可以同时执行,但F不能执行直到A、B、C、D和E都是完整的。
有不同的方法可以解决这个问题。让我们看一个简单的解决方案,它在主线程中创建一个休眠循环,定期检查返回值列表以确保满足某些条件。
def task_1
# Something slow
return results
end
def task_2
# Something slow
return results
end
def task_3
# Something slow
return results
end
my_responses = {}
Thread.new { my_responses[:result_1] = task_1 }
Thread.new { my_responses[:result_2] = task_2 }
Thread.new { my_responses[:result_3] = task_3 }
while (my_responses.count < 3) # Prevents the main thread from continuing until the three spawned threads are done and have dumped their results in the hash.
sleep(0.1) # This will cause the main thread to sleep for 100 ms between each check. Without it, you will end up checking the response count thousands of times pr. second which is most likely unnecessary.
end
# Any code at this line will not execute until all three results are collected.
请记住,多线程编程是一个棘手的主题,存在许多陷阱。使用 MRI 并没有那么糟糕,因为虽然 MRI 会在阻塞的线程之间愉快地切换,但 MRI 不支持同时执行两个线程,这解决了相当多的并发问题。
如果你想进入多线程编程,我推荐这本书:
http ://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601
它以 Java 为中心,但所解释的陷阱和概念是通用的。