Underc0de

Programación Scripting => Ruby => Mensaje iniciado por: Dharok en Febrero 23, 2010, 11:19:55 PM

Título: Hilos en Ruby
Publicado por: Dharok en Febrero 23, 2010, 11:19:55 PM
HILOS

Ruby permite poder realizar varios procesos al mismo tiempo. Esto lo hace mediante los hilos. Los hilos son una manera rapida y eficiente para aumentar el paralelismo en nuestros programas. Vamos pues a crear hilos en Ruby. Comenzar? con un sencillo ejemplo y luego pasar? a uno mas util.

CREAR HILOS:

Los hilos se crean con la llamada a Thread.new:

Código (ruby) [Seleccionar]
array=['hola', 'esto', 'es', 'un', 'ejemplo'] # Creamos un array de 5 elementos.

hilo=Array.new # Creamos un array para los hilos. Seria de 5 elementos y cada uno de ellos sera un hilo(Esto lo veremos a continuacion ;))


for element in array #creamos un bloque
   hilo<<Thread.new(element){|x| # Creamos un hilo para cada elemento y lo introducimos en el array hilo.
   puts x # Hacemos que imprima por pantalla x
   }
end

hilo.each{|z| z.join} # Con este ultimo comando, nos aseguramos de que los hilos no mueran antes de que finalize el programa principal (luego se explicara esto mas detenidamente).


Esto dara como resultado:
Citarhola
esto
es
un
ejemplo
Esto lo podemos hacer por medio del "each". Por ejemplo, el code anterior con "each" seria asi:
Código (ruby) [Seleccionar]
array=['hola', 'esto', 'es', 'un', 'ejemplo']

hilo= Array.new

array.each{|element|
   hilo<<Thread.new(element){|x|
   puts x
   }
}

hilo.each{|ele| ele.join}

Ahora vamos a pasar a hacer una aplicaci?n algo m?s compleja. Vamos a suponer un scanner de puertos que escanee de forma simultanea 3 dominios en un puerto determinado. Seria asi:
Código (ruby) [Seleccionar]
require 'socket'

paginas=['www.code-makers.es', 'www.google.com', 'www.youtube.com'] #Creamos el array con las 3 p?ginas.

hilo=Array.new

for pag in paginas
   hilo<<Thread.new(pag){|x|
   begin
   TCPSocket.new(x, 21)
   rescue
   puts "21 cerrado en #{x}"
   else
   puts "21 abierto en #{x}"
   end
   sleep 1 #Damos un peque?o espacio de tiempo para que se termine el proceso.
   begin
   TCPSocket.new(x, 80)
   rescue
   puts "80 cerrado en #{x}"
   else
   puts "80 abierto en #{x}"
   end
   sleep 1
   begin
   TCPSocket.new(x, 81)
   rescue
   puts "81 cerrado en #{x}"
   else
   puts "81 abierto en #{x}"
   end
   }
end

hilo.each{|ele| ele.join}

Esto dara como resultado:
Citar21 abierto en www.code-makers.es abierto.
21 abierto en www.google.com abierto.
21 abierto en www.youtube.com abierto.
80 abierto en www.code-makers.es abierto.
80 abierto en www.google.com abierto.
80 abierto en www.youtube.com abierto.
81 abierto en www.code-makers.es abierto.
81 abierto en www.google.com abierto.
81 abierto en www.youtube.com abierto.

MANIPULAR HILOS:

Existen diversos metodos de la clase Thread que sirven para controlar los hilos:
Código (ruby) [Seleccionar]
Thread.current # Nos devuelve el hilo que esta siendo ejecutado "<Thread:0x401bdf4c run>"
Thread.list # Nos devuelve un array con los objetos para cada hilo diciendonos si estan corriendo o parados.
   #<Thread:0x401b3e84 sleep>
   #<Thread:0x401b3f38 run>
   #<Thread:0x401b3fb0 sleep>
   #<Thread:0x401bdf4c run>
hilo.status # Nos devuelve el estatus del hilo. "run", "sleep", "aborting"
Thread.kill(hilo) # Acaba con el proceso.
Thread.start{|x| bloque} # Igual que Thread.new.
Thread.stop # Deja durmiendo al hilo pero no lo mata.
hilo.join # Cuando un programa en Ruby termina, los hilos en ejecuci?n se matan independientemente de su estado.
#Con join lo que hacemos es esperar a que termine el hilo antes de matarlos. Digamos que con join te aseguras de que se realicen todos los procesos.

VARIABLES:

En los hilos, las variables locales al hilo, son solo accesibles en el propio hilo, ya que no se comparten fuera del mismo. Es decir, las variables que se establecen en el propio hilo no tendran validez fuera. Si queremos utilizar variables del hilo fuera del mismo, es decir, si queremos que estas variables tengan validez fuera del hilo, tenemos que tratar el objeto del hilo como si fuera un hash, guardandolo mediante []= y llamandolo mediante []. Veamoslo:
Código (ruby) [Seleccionar]
count = 0
arr = []
10.times do |i|
  arr[i] = Thread.new {
    sleep(rand(0)/10.0)
    Thread.current["mycount"] = count # Asignamos la variable count a la llave del hash "mycount", que se guarda en hilo.
    count += 1
  }
end
arr.each {|t| t.join; print t["mycount"], ", " }
puts "count = #{count}"

Esto producira:
Citar8, 0, 3, 7 2, 1, 6, 5, 4, 9, count = 10


Vamos a poner otro ejemplo para que lo comprendeis mejor. Supongamos que tenemos un scanner de puertos y queremos imprimir la variable <a> fuera del hilo:
Código (ruby) [Seleccionar]
require 'socket'

paginas=['www.code-makers.es', 'www.google.com', 'www.youtube.com'] #Creamos el array con las 3 p?ginas.

hilo=Array.new

for pag in paginas
   hilo<<Thread.new(pag){|x|
   begin
   TCPSocket.new(x, 80)
   rescue
   a= "80 cerrado en #{x}"
   else
   a= "80 abierto en #{x}"
   end
   }
end

hilo.each{|ele| ele.join}
puts a

Esto daria como resultado un mensaje de error advirtiendonos de que la variable <a> no ha sido definida:
Citarhilos.rb:20: undefined local variable or method `a' for main:Object (NameError)
Para solventar este problema hariamos lo siguiente:
Código (ruby) [Seleccionar]
require 'socket'

paginas=['www.code-makers.es', 'www.google.com', 'www.youtube.com'] #Creamos el array con las 3 p?ginas.

hilo=Array.new

for pag in paginas
   hilo<<Thread.new(pag){|x|
   begin
   TCPSocket.new(x, 80)
   rescue
   a= "80 cerrado en #{x}"
   else
   a= "80 abierto en #{x}"
   end
   Thread.current["#{x}"]=a # Asignamos la variable <a> a la llave del hash x (que ser? la p?gina web que se est? escaneando en ese momento), y la guardamos en hilo.
   }
end


Esto nos daria un resultado positivo:
Citar80 abierto en www.code-makers.es

Creo que con estos dos ejemplos han quedado claras las variables.
Documentacion

Documentacion oficial.(http://www.ruby-doc.org/core/classes/Thread.html#M000472)
Mis documentacion.(http://pickaxe.ruby.org.es/tut_threads.html)
AUTOR: sh4van3