2

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

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(唉,目前没有选择更改它)

4

2 回答 2

6

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

您可以Concurrent通过指定1来控制使用线程的方式RubyThreadPoolExecutor

因此,就我而言,代码如下所示:

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 实际文档描述了执行此操作的不同方法,但实际代码与文档不一致,因此我在此处提供的代码是基于实际工作的代码。

于 2015-01-16T00:26:12.397 回答
0

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

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

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

于 2015-01-15T23:06:04.560 回答