在VIDEO_URL
中,有数千个视频需要下载。我想使用线程来完成这项工作,但一次最多限制为十个线程。我怎么能重写以下代码才能得到它?
VIDEO_URL.each do | video |
@workers << Thread.new{dl_video(video)}
end
@workers.each { |t| t.join }
在工作线程超过10之后,gem 线程池似乎不会被阻塞,是否I / O块使线程池无效?
如果我下载没有线程池的视频,效果会很好。
但是如果我用线程池下载视频,视频就不会被下载,当有10个工作者时,主线程应该被阻止,但它没有。 (每个视频至少应该有1分钟下载)
MAX_WORKERS = 10
@pool = Thread.pool(MAX_WORKERS)
def dl_video(video)
File.open(video["title"], "wb") do |saved_file|
@pool.process{
saved_file.write open(video["link"], :allow_redirections => :safe).read
# saved_file.write(HTTParty.get(video["link"]).parsed_response)
}
end
end
答案 0 :(得分:2)
您尝试实现的是经常使用的模式,它被称为线程池。
我还没有尝试过,但也许threadpool宝石或类似的东西值得研究:
require "threadpool"
pool = ThreadPool.new(10)
VIDEO_URL.each{|video| pool.process{dl_video(video)}}
答案 1 :(得分:2)
您想要的是一个线程池。 Ruby的线程有extension,其中包含此功能。
未经测试的代码段直接改编自库示例:
require 'thread/pool'
# Create thread pool with up to 10 simultaneous running threads
pool = Thread.pool(10)
VIDEO_URL.each do | video |
# Add each download task the the thread pool
pool.process do
dl_video(video)
end
end
# Block and wait for the thread pool to run out of tasks
pool.shutdown
答案 2 :(得分:1)
一个简单的解决方案(不涉及任何新的宝石)将启动10个pop
并处理数组中第一个URL的线程。
[].tap do |threads|
urls = VIDEO_URLS.clone
semaphore = Mutex.new
number_of_threads = 10
number_of_threads.times do
threads << Thread.new do
until urls.empty?
url = semaphore.synchronize { urls.pop }
download_video(url)
end
end
end
end.each(&:join)
另一种解决方案可能是将阵列分成不同的切片(10个或更少);有不同的方法可以做到这一点。之后,每个线程都可以处理每个切片。代码可能总体上更长,但如果你愿意,你可以摆脱Mutex
。
[].tap do |threads|
slices # split VIDEO_URLS into required slices. leave this up to you.
slices.each do |urls|
threads << Thread.new do
urls.each { |url| download_video(url) }
end
end
end.each(&:join)
答案 3 :(得分:0)
您可以使用each_slice。
VIDEO_URL.each_slice(10) do | batch |
batch.each do |video|
@workers << Thread.new{dl_video(video)}
end
@workers.each { |t| t.join }
@workers = []
end