Hilos en Ruby (by sh4van3)

Iniciado por ANTRAX, Febrero 24, 2010, 04:18:19 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Febrero 24, 2010, 04:18:19 PM Ultima modificación: Julio 31, 2014, 10:15:50 PM por Expermicid
HILOS

Ruby permite poder realizar varios procesos al mismo tiempo. Esto lo hace mediante los hilos. Los hilos son una manera r?pida 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 m?s ?til.

CREAR HILOS:

Los hilos se crean con la llamada a No tienes permitido ver los links. Registrarse o Entrar a mi cuenta:

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

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


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 explicar? esto m?s detenidamente).


Esto dar?a como resultado:
Citarhola
esto
es
un
ejemplo

Esto lo podemos hacer por medio del "each". Por ejemplo, el code anterior con "each" ser?a as?:

Código: ruby
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 simult?nea 3 dominios en un puerto determinado. Ser?a as?:

Código: ruby
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 dar? como resultado:

Citar21 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.
21 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.
21 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.
80 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.
80 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.
80 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.
81 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.
81 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.
81 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta abierto.

MANIPULAR HILOS:

Existen diversos m?todos de la clase Thread que sirven para controlar los hilos:

Código: ruby
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 tendr?n 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, guard?ndolo mediante []= y llamandolo mediante []. Ve?moslo:

Código: ruby
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 producir?a:
Citar8, 0, 3, 7 2, 1, 6, 5, 4, 9, count = 10


Vamos a poner otro ejemplo para que lo comprend?is mejor. Supongamos que tenemos un scanner de puertos y queremos imprimir la variable <a> fuera del hilo:

Código: ruby
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 dar?a 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 har?amos lo siguiente:

Código: ruby
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

hilo.each{|ele| ele.join; puts ele["www.code-makers.es"]}


Esto nos dar?a un resultado positivo:
Citar80 abierto en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

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

Documentaci?n:

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta