如何限制并发线程数

时间:2015-05-07 07:50:36

标签: ruby multithreading threadpool

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

4 个答案:

答案 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