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:
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:
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:
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:
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:
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: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:
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.
DocumentacionDocumentacion oficial.(http://www.ruby-doc.org/core/classes/Thread.html#M000472)
Mis documentacion.(http://pickaxe.ruby.org.es/tut_threads.html)
AUTOR: sh4van3