我应该如何使用 Concurrent-Ruby 限制线程创建?

How ought I limit Thread creation using Concurrent-Ruby?

我有一个进程使用 concurrent-ruby gem 使用 Concurrent::Future.execute 同时处理大量 API 调用,一段时间后,它死了:

ERROR -- : can't create Thread (11) (ThreadError)
/current/vendor/bundler_gems/ruby/2.0.0/bundler/gems/concurrent-ruby-cba3702c4e1e/lib/concurrent/executor/ruby_thread_pool_executor.rb:280:in `initialize'

有没有一种简单的方法可以告诉 Concurrent 限制它生成的线程数,因为我无法提前知道它需要多少次 API 调用制作?

或者这是我需要在我的应用程序中明确编码的内容吗?

我正在使用 Ruby 2.0.0(唉,目前无法更改它)

对此的典型答案是创建一个线程池。

创建有限数量的线程,有办法记录哪些是活动的,哪些不是。当一个线程完成一个 API 调用时,将其标记为非活动状态,以便它可以处理下一个调用。

您正在使用的 gem 已经有线程池。

经过一些阅读和反复试验,我得出了以下解决方案。在这里发帖以防对其他人有帮助。

您可以通过指定 RubyThreadPoolExecutor1

来控制 Concurrent 使用线程的方式

因此,在我的例子中,代码如下所示:

threadPool = Concurrent::ThreadPoolExecutor.new(
  min_threads: [2, Concurrent.processor_count].min,
  max_threads: [2, Concurrent.processor_count].max,
  max_queue:   [2, Concurrent.processor_count].max * 5,
  overflow_policy: :caller_runs
)

result_things = massive_list_of_things.map do |thing|
  (Concurrent::Future.new executor: threadPool do
    expensive_api_call using: thing
  end).execute
end

所以在我的笔记本电脑上我有 4 个处理器,所以这样它将使用 2 到 4 个线程,并在强制执行使用调用线程之前允许队列中最多 20 个线程。当线程释放时,并发库将重新分配它们。

然而,为 max_queue 值选择正确的乘数看起来像是一个反复试验的问题;但 5 是一个合理的猜测。

1 实际文档描述了一种不同的方法,但实际代码与文档不一致,所以我在这里展示的代码是基于实际有效的方法。