首页 文章

如何限制并发线程数

提问于
浏览
3

VIDEO_URL 中,有数千个视频要下载 . 我想使用线程来完成这项工作,但一次最多限制为十个线程 . 我怎么能重写以下代码才能得到它?

VIDEO_URL.each do | video |
  @workers << Thread.new{dl_video(video)}
end
@workers.each { |t| t.join }

更新

工作线程超过10后,gem thread pool 似乎没有被阻塞,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

    您要实现的是经常使用的模式,它被称为线程池 .

    我没有尝试过,但也许threadpool宝石或类似的东西值得研究:

    require "threadpool"
    
    pool = ThreadPool.new(10)
    VIDEO_URL.each{|video| pool.process{dl_video(video)}}
    
  • 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
    
  • 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)
    
  • 2

    你可以使用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
    

相关问题