在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”)