【问题标题】:How to limit the number of concurrent threads如何限制并发线程数
【发布时间】:2015-07-17 15:57:18
【问题描述】:

VIDEO_URL,有成千上万的视频可供下载。我想使用线程来完成这项工作,但一次最多只能使用十个线程。怎样重写下面的代码才能得到呢?

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

更新

gem的线程池在工作线程超过10个后好像没有阻塞,是不是让线程池的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

【问题讨论】:

    标签: ruby multithreading threadpool


    【解决方案1】:

    您尝试实现的是一种常用的模式,它被称为线程池

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

    require "threadpool"
    
    pool = ThreadPool.new(10)
    VIDEO_URL.each{|video| pool.process{dl_video(video)}}
    

    【讨论】:

    • 感谢帮助,gem threadpool 工作线程超过10个后好像没有阻塞,是不是让线程池的I/O阻塞没有效果?
    【解决方案2】:

    你想要的叫做线程池。 Ruby 的线程有一个extension,其中包含此功能。

    直接改编自库示例的未经测试的 sn-p:

    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
    

    【讨论】:

    • @ibr gem 线程池在工作线程超过10个后好像没有阻塞,是不是让线程池的I/O阻塞没有效果?
    • 对不起,我不明白你的问题。
    • 嗨我更新了我的描述,我的意思是如果我把任务放在线程池中,视频将不会被下载
    • 我认为您使用的是提到的 gem sawa,而不是我使用的那个。我已经在回复中测试了代码,pool.shutdown 块很好。
    【解决方案3】:

    一个简单的解决方案(不涉及任何新的 gem)是启动 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)
    

    【讨论】:

    • 你的第一个例子是一个非常小的线程池实现:-)
    • 是的。基本上,与线程池相同,但没有添加任何额外的 gem。编辑:我注意到我的帖子中的错误。当它应该是“不涉及线程池宝石”时,它说“不涉及线程池”。已更正。 :)
    【解决方案4】:

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

    【讨论】:

    • 这种方法将创建最多十个同时下载,等待所有十个完成,然后创建新线程,等待它们完成等等。这可能不是最佳的:)
    • 我同意你的观点,这不是最好的方式。
    猜你喜欢
    • 1970-01-01
    • 2011-09-14
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-25
    相关资源
    最近更新 更多