2010-10-25 3 views
6

Je regardais les capacités de traitement parallèle/asynchrone de ruby ​​et lisais de nombreux articles et articles de blog. Je regardais à travers eventmachine, fibres, Revactor, Reia, etc, etc. Malheureusement, je n'ai pas pu trouver une solution simple, efficace (et non-IO-blocage) pour cette utilisation très simple cas:Ruby concurrent/traitement asynchrone (avec un cas d'utilisation simple)

File.open('somelogfile.txt') do |file| 
    while line = file.gets  # (R) Read from IO 
    line = process_line(line) # (P) Process the line 
    write_to_db(line)   # (W) Write the output to some IO (DB or file) 
    end 
end 

est-ce que vous pouvez voir, mon petit script effectue trois opérations lues (R), processus (P) & écrire (W). Supposons - pour simplifier - que chaque opération prend exactement 1 unité de temps (par exemple 10ms), le code actuel serait donc quelque chose comme ça (5 lignes):

Time:  123456789(15 units in total) 
Operations: RPWRPWRPWRPWRPW 

Mais, je voudrais qu'il fasse quelque chose comme ceci:

Time:  1234567 (7 units in total) 
Operations: RRRRR 
      PPPPP 
       WWWWW 

Évidemment, je peux exécuter trois procédés (lecteur, le processeur & écrivain) et transmettre lignes de lecture de lecteur dans la file d'attente de processeur et ensuite passer les lignes traitées dans la file d'attente d'écriture (tous coordonnés par exemple RabbitMQ) . Mais, le cas d'utilisation est si simple, il ne se sent pas juste.

Des indices sur la façon dont cela pourrait être fait (sans passer de Ruby à Erlang, à Closure ou à Scala)?

+1

Les écritures doivent-elles être appelées dans le même ordre qu'elles ont été lues? –

+0

Non, c'est tout le point qu'ils peuvent être complètement asynchrone. – Dim

Répondre

1

Vérifiez la pêche (http://peach.rubyforge.org/). Faire un parallèle "chacun" ne pouvait pas être plus simple. Cependant, comme le dit la documentation, vous devrez exécuter sous JRuby pour utiliser le thread natif de la JVM.

Voir la réponse de Jorg Mittag à this SO question pour beaucoup de détails sur les capacités de multithreading des différents interprètes de Ruby.

+0

Hmm, la pêche n'est pas vraiment ce que je cherche. Je ne veux pas exécuter le RPW en parallèle, je veux détacher la tâche les uns des autres et les exécuter de manière asynchrone. La réponse de Jorg Mittag donne une excellente introduction. Je suis bien au courant des options offertes, mais aucun d'entre eux semble avoir une réponse à mon problème. – Dim

3

Si vous avez besoin qu'il soit vraiment parallèle (à partir d'un seul processus), je crois que vous devrez utiliser JRuby pour obtenir des vrais threads natifs et pas de GIL.

Vous pouvez utiliser quelque chose comme DRB pour distribuer le traitement entre plusieurs processus/cœurs, mais pour votre utilisation, c'est un peu trop. , Vous pourriez plutôt essayer d'avoir plusieurs processus en utilisant des tuyaux communicating:

$ cat somelogfile.txt | ruby ./proc-process | ruby ./proc-store 

Dans ce scénario chaque pièce est son propre processus qui peut fonctionner en parallèle mais la communication en utilisant STDIN/STDOUT. C'est probablement l'approche la plus facile (et la plus rapide) de votre problème.

# proc-process 
while line = $stdin.gets do 
    # do cpu intensive stuff here 
    $stdout.puts "data to be stored in DB" 
    $stdout.flush # this is important 
end 

# proc-store 
while line = $stdin.gets do 
    write_to_db(line) 
end 
+1

Je pensais que GIL Ruby 1.9 vous permet de faire des choses CPU dans un thread alors qu'un autre thread fait des E/S - c'est-à-dire, il interdit seulement deux threads faire des choses CPU. –

+0

Parlez-vous des fibres?Ma compréhension limitée des Fibres est qu'au lieu de threads qui ont chacun une quantité de temps CPU partagée, votre code transmet explicitement le traitement à la fibre qui peut gérer l'opération d'E/S bloquante et retourner immédiatement au code appelant. Cela réduit le temps que vous passez à attendre, mais je ne pense pas que cela vous permettra de couvrir plus d'un processeur par processus. Je pense que le GIL signifie qu'un seul thread d'exécution peut fonctionner à tout moment. http://www.igvita.com/2009/05/13/fibers-cooperative-scheduling-in-ruby/ – JEH

+2

L'utilisation de tuyaux est une bonne solution pour diviser le problème en 3 processus distincts, mais il n'est pas asynchrone. Il s'agit en fait d'une "solution de contournement Ruby", donc assez difficile à mettre en œuvre dans le cadre d'une application plus importante. Le "problème" que j'ai décrit ci-dessus est un exemple simple de traitement piloté par les E/S. J'essaie de comprendre ce que Ruby est capable de faire dans ce domaine et ce qui pourrait lui manquer. – Dim