2012-01-08 3 views
7

나는 URL 배열을 가지고 있으며 각각 하나를 열고 특정 태그를 가져 오지 않을 것이다.
하지만 저는 이것을 병렬로하고 싶습니다.루비의 병렬 HTTP 요청

 
urls = [...] 
tags = [] 
urls.each do |url| 
    fetch_tag_asynchronously(url) do |tag| 
    tags << tag 
    end 
end 
wait_for_all_requests_to_finish() 

이 좋지 않을까 멋지고 안전한 방법으로 수행 할 수있는 경우 : 여기

내가 뭘 원하는지에 대한 의사입니다.
스레드를 사용할 수 있지만 배열이 루비의 스레드로부터 안전하지 않은 것처럼 보입니다.

답변

25

당신은 사용하여 스레드 안전을 보장 할 수있는 Mutex :

require 'thread' # for Mutex 

urls = %w(
    http://test1.example.org/ 
    http://test2.example.org/ 
    ... 
) 

threads = [] 
tags = [] 
tags_mutex = Mutex.new 

urls.each do |url| 
    threads << Thread.new(url, tags) do |url, tags| 
    tag = fetch_tag(url) 
    tags_mutex.synchronize { tags << tag } 
    end 
end 

threads.each(&:join) 

그것은 그러나 비생산적 모든 URL에 대한 새 스레드를 사용하여,이보다 확대됨 수 있습니다 같은 스레드의 수를 제한 할 수 있습니다 : 루비의 GIL에

THREAD_COUNT = 8 # tweak this number for maximum performance. 

tags = [] 
mutex = Mutex.new 

THREAD_COUNT.times.map { 
    Thread.new(urls, tags) do |urls, tags| 
    while url = mutex.synchronize { urls.pop } 
     tag = fetch_tag(url) 
     mutex.synchronize { tags << tag } 
    end 
    end 
}.each(&:join) 
+0

하하, 이것은 내가 작성한 똑같은 해결책이었습니다! :) –

+0

대량의 작업이 IO 인 경우 코어 수는 실제로 중요하지 않아야합니까? – ben

+0

@ben : 사실입니다. 여전히 너무 많은 쓰레드와 열린 커넥션을 가지고 있으면 생산성이 떨어질 수 있습니다. 파이어 폭스는 HTTP 파이프 라이닝에 8 개의 기본 연결을 사용한다고 생각하기 때문에이 값을 5 대신에 제안 된 값으로 사용합니다. –

7

Typhoeus/Hydra 보석 조합은 이것을 매우 쉽게 수행하도록 설계되었습니다. 매우 편리하고 강력합니다.

+1

스레드로부터 안전하지 않음 – Imnl