2016-12-06 3 views
0

내가 처음에 (사람)에 시도한 50 + GB의 XML 파일이 노코 기리 :왜이 예에서 루비 스레드 역할을 수행 - 효율적으로 누락 된 파일

있어 killed: 9 취급 - 분명 :)

지금 나는 (그것을)이 자상와 진흙 루비 스레드 물로 해요 : 결과 파일을 작성할 때

#!/usr/bin/env ruby 

def add_vehicle index, str 
    IO.write "ess_#{index}.xml", str 
    #file_name = "ess_#{index}.xml" 
    #fd = File.new file_name, "w" 
    #fd.write str 
    #fd.close 
    #puts file_name 
end 

begin 

    record = [] 
    threads = [] 
    counter = 1 
    file = File.new("../ess2.xml", "r") 
    while (line = file.gets) 
    case line 
    when /<ns:Statistik/       
     record = [] 
     record << line 
    when /<\/ns:Statistik/       
     record << line 
     puts "file - %s" % counter 
     threads << Thread.new { add_vehicle counter, record.join } 
     counter += 1 
    else 
     record << line 
    end 
    end 
    file.close 
    threads.each { |thr| thr.join } 
rescue => err 
    puts "Exception: #{err}" 
    err 
end 

는 어떻게 든이 코드는 '건너 뛰고'하나 개 또는 두 개의 파일 - 흠!?

+0

그냥 호기심. 이 파일은 무엇입니까? 나는 그 노드를 찾았고, 덴마크의 자동차 부품 목록을 발견했다. 또는 뭔가. –

+0

큰 파일을 쓰다 버리려고하면 모든 언어가 중단됩니다. 대신 XML을 파싱 할 때 [Nokogiri가 구현하는 SAX 파서] (http://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/SAX)를 사용해야합니다. 나는 그것을 사용하는 방법에 대해 읽는 것이 좋습니다. –

답변

1

파일이 크기 때문에 멀티 스레딩을 사용하고 싶습니다.

이제 알려 드리겠습니다. problemstwo

더 심각한 점은이 code과 관련하여 매우 좋은 경험이었습니다.

메모리 사용량이 거의없는 20GB xml 파일을 구문 분석했습니다.

는 언급 코드를 다운로드 xml_parser.rb로 저장하고이 스크립트가 작동한다 : 그것은 시간이 걸릴 것

require_relative 'xml_parser.rb' 

file = "../ess2.xml" 

def add_vehicle index, str 
    filename = "ess_#{index}.xml" 
    File.open(filename,'w+'){|out| out.puts str} 
    puts format("%s has been written with %d lines", filename, str.each_line.count) 
end 

i=0 
Xml::Parser.new(Nokogiri::XML::Reader(open(file))) do 
    for_element 'ns:Statistik' do 
    i+=1 
    add_vehicle(i,@node.outer_xml) 
    end 
end 

#=> ess_1.xml has been written with 102 lines 
#=> ess_2.xml has been written with 102 lines 
#=> ... 

하지만 오류없이 많은 메모리를 사용하지 않고 작동합니다.

threads = [] 
counter = 1 
threads << Thread.new { puts counter } 
counter += 1 
threads.each { |thr| thr.join } 
#=> 2 

threads = [] 
counter = 1 
threads << Thread.new { puts counter } 
sleep(1) 
counter += 1 
threads.each { |thr| thr.join } 
#=> 1 

counter += 1

add_vehicle 전화보다 더 빨리했다 : 그런데

는 여기에 코드가 일부 파일을 놓친 이유입니다. 따라서 add_vehicle은 종종 잘못된 카운터로 호출되었습니다. 수백만 개의 노드가있는 경우 일부는 0 오프셋을 얻을 수 있고, 일부는 1 오프셋을 얻을 수 있습니다. 동일한 ID로 2 add_vehicle을 호출하면 서로 겹쳐 쓰며 파일이 누락됩니다.

record과 같은 문제가 있습니다. 줄 바꿈이 잘못된 파일로 기록됩니다.

+0

안녕하세요 @ 에릭 - duminil - 그래서 당신이 차임을 좋아해요 :) 당신은 Thread.new가 설치되기 전에 counter 변수로 counter + = 1 'messes'라고 말하는 것을 의미합니까? add_vehicle? –

+0

이것은 매우 시원합니다! 공유해 주셔서 대단히 감사합니다! –

+0

@walt_die : 정확하게. 그것은 경쟁 조건이라고합니다 : http://stackoverflow.com/questions/34510/what-is-a-race-condition 그리고 잘, 그것은 짜증납니다. –

1

Perhabs counter += 1과 뮤텍스를 동기화해야합니다. 예 : @lock = Mutex.new @counter = 0 def add_vehicle str @lock.synchronize do @counter += 1 IO.write "ess_#{@counter}.xml", str end end Mutex는 여러 동시 스레드의 공유 데이터에 대한 액세스를 조정하는 데 사용할 수있는 간단한 세마포를 구현합니다.

또는 처음부터 다른 방법으로 이동하여 Ox을 사용할 수 있습니다. Nokogiri보다 빠른 방법입니다. comparison을 살펴보십시오. 거대한 파일 들어. Ox::Sax

+0

들으 : 사실은 범인에게 자신을 수 :) 여기에 아마 당신의 DSL이 https://github.com/ohler55/ox 결혼 할 수 reference_ _by과 급격하게 빠르고 기념비 우아한 _baby_을 가지고있어 계획 없다 제안 - Ox 구문 분석기는 실제로 유망 해 보입니다! –