Ruby on rails Ruby中的线程安全枚举器 TLDR:Ruby中有线程安全版本的枚举器类吗? 我想做的是:

Ruby on rails Ruby中的线程安全枚举器 TLDR:Ruby中有线程安全版本的枚举器类吗? 我想做的是:,ruby-on-rails,ruby,multithreading,lazy-loading,enumerator,Ruby On Rails,Ruby,Multithreading,Lazy Loading,Enumerator,我在RubyonRails应用程序中有一个方法,我想同时运行。该方法应该创建一个包含站点报告的zip文件,其中zip中的每个文件都是PDF。从html到PDF的转换有点慢,因此需要多线程 我希望如何做到这一点: 我想使用5个线程,所以我想在线程之间应该有一个共享枚举器。每个线程都会从枚举器中弹出一个值,并对其运行do stuff。以下是我的想法: t=Zip::OutputStream::write|u buffer do|z| mutex=mutex.new gen=枚举数。新{124; g|

我在RubyonRails应用程序中有一个方法,我想同时运行。该方法应该创建一个包含站点报告的zip文件,其中zip中的每个文件都是PDF。从html到PDF的转换有点慢,因此需要多线程

我希望如何做到这一点: 我想使用5个线程,所以我想在线程之间应该有一个共享枚举器。每个线程都会从枚举器中弹出一个值,并对其运行do stuff。以下是我的想法:

t=Zip::OutputStream::write|u buffer do|z|
mutex=mutex.new
gen=枚举数。新{124; g|
报告.all.includes(“员工”=>[“老板”、“客户”],“项目”=>{“项目所有者”=>[“项目团队”]})。查找每个do |报告|
g、 产量报告
结束
}
5.times.map{
新做的
开始
环道
mutex.do
@报告=下一代
结束
title=@report.title+“@report.id.to”
title+=“.pdf”,除非title.end_以“.pdf”结尾
pdf=PDFKit.new(将_呈现为_字符串(:template=>partial_url,局部变量:{array:[@report]},
:layout=>false))。到
mutex.do
z、 放置下一个条目(标题)
z、 写作(pdf)
结束
结束
救援停止迭代
#无所事事
结束
结束
}.each{thread | thread.join}
结束
当我尝试时发生了什么: 当我运行上述代码时,出现以下错误:

FiberError at /generate_report
fiber called across threads
经过一些搜索之后,我发现了一个问题,建议我使用队列而不是枚举器,因为队列是线程安全的,而枚举器不是。虽然这对于非Rails应用程序可能是合理的,但对我来说是不切实际的

为什么我不能只使用队列: Rails4ActiveRecord的一个优点是,在对查询进行迭代之前,它不会加载查询。而且,如果您使用类似于
find_each
的方法对其进行迭代,它会以1000个为一批进行迭代,因此您永远不必一次将整个表存储在ram中。我使用的查询结果:
Report.all.includes(“employee”=>[“boss”,“client”],“projects”=>{“project\u owner”=>[“project\u team”]})
很大。非常大。我需要能够动态加载它,而不是执行以下操作:

gen=Report.all.includes(“employee”=>[“boss”,“client”],“projects”=>{“project\u owner”=>[“project\u team”]}).map(&queue.method(:push))
这将把整个查询加载到ram中

最后一个问题是: 是否有线程安全的方法来执行此操作:

gen=Enumerator.new{|g|
报告。全部。包括(…)。查找每个do报告|
g、 产量报告
结束
}

这样我就可以跨多个线程从
gen
弹出数据,而不必将整个
报告(以及所有包含的)表加载到ram中?

如果在填充队列之前启动工作线程,它们将在填充队列时开始消耗队列,因为根据经验,网络比CPU慢,每批应在下一批到达时(主要)消耗:

queue = Queue.new

t1 = Thread.new do
  while !queue.empty?
    p queue.pop(true)
    sleep(0.1)
  end
end
t2 = Thread.new do
  while !queue.empty?
    p queue.pop(true)
    sleep(0.1)
  end
end

(0..1000).map(&queue.method(:push))

t1.join
t2.join
如果仍然太慢,您可以选择使用,这将阻止
推送,如果队列达到足够大的大小:

queue = SizedQueue.new(100)

t1 = Thread.new do
  while !queue.empty?
    p "#{queue.pop(true)} - #{queue.size}"
    sleep(0.1)
  end
end
t2 = Thread.new do
  while !queue.empty?
    p queue.pop(true)
    sleep(0.1)
  end
end
(0..300).map(&queue.method(:push))
t1.join
t2.join