在ruby中一次读取N行文件

我有一个大文件(数百兆),由文件名组成,每行一个。

我需要循环遍历文件名列表,并为每个文件名分叉一个进程。 我希望一次最多8个分叉进程,我不想一次将整个文件名列表读入RAM。

我甚至不确定从哪里开始,任何人都可以帮助我吗?

File.foreach("large_file").each_slice(8) do |eight_lines| # eight_lines is an array containing 8 lines. # at this point you can iterate over these filenames # and spawn off your processes/threads end 

听起来Process模块对此任务非常有用。 这是我快速拼凑起来的一个起点:

 include Process i = 0 for line in open('files.txt') do i += 1 fork { `sleep #{rand} && echo "#{i} - #{line.chomp}" >> numbers.txt` } if i >= 8 wait # join any single child process i -= 1 end end waitall # join all remaining child processes 

输出:

你好
再见

 TEST1
 TEST2
一个
 b
 C
 d
 Ë
 F
 G
 $ ruby​​ b.rb
 $ cat numbers.txt 
 1  - 你好
 3  -  
 2  - 再见
 5  -  test2
 6  -  a
 4  -  test1
 7  -  b
 8  -  c
 8  -  d
 8  -  e
 8  -  f
 8克

这种方式的工作原理是:

  • for line in open(XXX)将懒惰地迭代您指定的文件的行。
  • fork将生成执行给定块的子进程,在这种情况下,我们使用反引号来指示shell要执行的操作。 请注意, rand在这里返回一个值0-1,所以我们睡不到一秒钟,我调用line.chomp来删除我们从line获得的尾随换行符。
  • 如果我们累积了8个或更多进程,请调用wait来停止所有进程,直到其中一个进程返回。
  • 最后,在循环外部,在退出脚本之前调用waitall加入所有剩余进程。

这里的Mark的解决方案包含在ProcessPool类中,可能对它有帮助(如果我犯了一些错误,请纠正我):

 class ProcessPool def initialize pool_size @pool_size = pool_size @free_slots = @pool_size end def fork &p if @free_slots == 0 Process.wait @free_slots += 1 end @free_slots -= 1 puts "Free slots: #{@free_slots}" Process.fork &p end def waitall Process.waitall end end pool = ProcessPool.new 8 for line in open('files.txt') do pool.fork { Kernel.sleep rand(10); puts line.chomp } end pool.waitall puts 'finished' 

Queue的标准库文档有

 require 'thread' queue = Queue.new producer = Thread.new do 5.times do |i| sleep rand(i) # simulate expense queue << i puts "#{i} produced" end end consumer = Thread.new do 5.times do |i| value = queue.pop sleep rand(i/2) # simulate expense puts "consumed #{value}" end end consumer.join 

我确实发现它有点冗长。

维基百科将此描述为线程池模式

arr = IO.readlines(“filename”)