Mini - Ladrillo
En determinadas ocasiones, resulta necesario ejecutar varias tareas al mismo tiempo para ganar en velocidad de proceso. 
Estas tareas pueden ser similares o bien diferentes y a veces utilizan el mismo recurso simultaneamente o bien necesitan comunicarse entre ellos para coordinarse; esto puede dar problemas, pero la programación multihilo nos permite solucionar estas situaciones mediante varios recursos, cada uno adecuado o necesario en un caso u otro. 
Estos recursos son :
- Semáforos. Permiten acceder a un recurso y liberarlo cuando queramos.
- Bloqueos. Bloquean el recurso hasta que es liberado para su reutilización por parte de otro hilo.
- Condiciones. Este caso es similar a los anteriores, pero permite notificar cuando el recurso es liberado.
En los 3 casos, podemos definir el número de accesos simultáneos. Podría ser 1, 2 o los que queramos, siempre y cuando el recurso a utilizar aguante dicha carga sin fallar.
El Código
Comenzamos con algo sencillo: un solo hilo que se ejecuta de forma simultánea al hilo principal.
#!/usr/bin/python

import threading
import time


def hilo(i):
"""
:param i: numero de hilo a efectos ilustrativos
:return: nada
"""
print "[+] En hilo %d\n" % i
time.sleep(3)
print "[-] hilo %d finalizado\n" % i

# Creacion y Ejecucion de 1 hilo paralelo a hilo 2

simplethread=threading.Thread(target=hilo, args=[1])
simplethread.start()

# Esto se ejecuta como proceso principal

hilo(2)
# Esperamos a que acabe el hilo paralelo 1
simplethread.join()
En este caso, no hay interacción ni compartimos recursos: 1 hilo, 1 proceso principal.
En el siguiente caso, ya, vamos a ejecutar varios hilos simultáneamente. No compartimos recursos ni hay sincronía alguna entre procesos.
#!/usr/bin/python

import threading
import time

NTHREADS=20

def hilo(i):
"""
:param i: numero de hilo a efectos ilustrativos
:return: nada
"""
print "[+] En hilo %d" % i
time.sleep(3)
print "[-] hilo %d finalizado" % i


simplethread=[]
for i in range(NTHREADS):
# arranque y comienzo de hilo num i+1
simplethread.append(threading.Thread(target=hilo, args=[i+1]))
simplethread[-1].start()

for i in range(NTHREADS):
# esperamos que acabe el hilo num i
simplethread[i].join()

print "[*] all threads finished"
En este ejemplo, usamos semáforos, que son la forma mas primitiva de compartir recursos. El ejemplo consiste en la salida por un tunel por parte de varias personas. Solo puede entrar una a la vez.
#!/usr/bin/python

import threading
from threading import Semaphore
import time

NTHREADS=20
WIDTH=1

sem=Semaphore(WIDTH)

def hilo(i):
"""
:param i: numero de hilo a efectos ilustrativos
:return: nada
"""
print "[+] En hilo %d" % i
time.sleep(3)
sem.acquire()
print "[+] En tunel hilo %d" % i
time.sleep(1)
sem.release()
print "[-] hilo %d, estoy fuera" % i

simplethread=[]
for i in range(NTHREADS):
# arranque y comienzo de hilo num i+1
simplethread.append(threading.Thread(target=hilo, args=[i+1]))
simplethread[-1].start()

for i in range(NTHREADS):
# esperamos que acabe el hilo num i
simplethread[i].join()

print "[*] all
El siguiente ejemplo, ilustra los bloqueos de recursos. En este caso RLock.
Simula varias personas bebiendo de un solo vaso. Todos beben a la misma velocidad.
#!/usr/bin/python

import threading
from threading import RLock
import time

NTHREADS=20

# Bloqueo de un recurso mientras se usa
sem=RLock()

def hilo(i):
"""
:param i: numero de hilo a efectos ilustrativos
:return: nada
"""
print "[+] En hilo %d" % i
time.sleep(3)
with sem:
print "[+] Bebiendo hilo %d..." % i
time.sleep(1)
print "[-] hilo %d satisfecho" % i

simplethread=[]
for i in range(NTHREADS):
# arranque y comienzo de hilo num i+1
simplethread.append(threading.Thread(target=hilo, args=[i+1]))
simplethread[-1].start()

for i in range(NTHREADS):
# esperamos que acabe el hilo num i
simplethread[i].join()

print "[*] all threads finished"
Para terminar, ilustraré las Conditions. Estas formas de bloqueo de recursos son las más sofisticadas que hay. En el ejemplo, seguimos bebiendo, esta vez, cada uno bebe a su ritmo y notifica cuando terminó de beber al resto.
#!/usr/bin/python

import threading
from threading import Condition
import time
from random import randint

NTHREADS=20

# Bloqueo de un recurso mientras se usa
sem=Condition()

def bebe(i):
s=randint(1,10)
print "[+] Bebiendo hilo %d.. esperate %d secs" % (i,s)
time.sleep(s)
sem.notify(1)
print "[-] estaba de muerte, hilos --%d--" % i
sem.release()

def hilo(i):
"""
:param i: numero de hilo a efectos ilustrativos
:return: nada
"""
print "[+] En hilo %d" % i
time.sleep(1)
while not sem.acquire():
sem.wait(0.5)
else:
bebe(i)
print "[-] hilo %d finalizado" % i

simplethread=[]
for i in range(NTHREADS):
# arranque y comienzo de hilo num i+1
simplethread.append(threading.Thread(target=hilo, args=[i+1]))
simplethread[-1].start()

for i in range(NTHREADS):
# esperamos que acabe el hilo num i
simplethread[i].join()

print "[*] all threads finished"
El código de todos los ejemplos lo podeis encontrar aquí demos_multihilo.tar.gz
La mejor forma de aprender, es modificando los ejemplos y ejecutándolos.
Espero que os haya interesado el artículo y muchas gracias por la lectura.
Saludos y hasta la proxima.
Current rating: 4.1
  • Share

Comments

sergio 2 years, 10 months ago

Hola.
como se podrían ejecutar 16 temporizadores a la vez ?
bueno por diferencia de segundos.
El problema es que al arrancar un motor se debe temporizar 35 minutos; pero puede ser que se arranque otro motor en cualquier instante y temporise otros 35 minutos, sin afectar al temporizador anterio, etc.
Lo quise intentar con un arduino pero no es posible.
De antemano gracias.

Link | Reply
Currently unrated

javier 2 years, 10 months ago

hola Sergio,

En Python es facil hacer eso. Solo tienes que usar un array de 16 posiciones que contenga la hora de comienzo de ejecución del motor y chequear ese array para ver si sobrepasó alguno los 35 minutos. En ese caso, el motor se apagaría y se resetearía el contador a None.

No tiene que ver del todo con multihilo pero bueno, podría ser que si, por eso te contesto al off-topic.

No sé si me he explicado bien. Si tienes dudas, comenta.
Un saludo

Link | Reply
Currently unrated

sergio 2 years, 10 months ago

Hola, gracias por contestar.
Estoy de acuerdo en tú solución, de hecho había pensado algo parecido, solo que no conosco muy bien python. Y mi idea era o es tomar en tiempo real la hora del sistema y sumarle los minutos, (tiempo_futuro = tiempo_sistema + 35 minutos) y luego comparar este tiempo.
si sitema > tiempo_futuro entonces apagar motor.
Aunque todo esto se tiene que manejar con hilos, estás de acuerdo?
Como no conosco python, no se como hacer la rutina,
lo he intentado, pero no puedo trabajarla en tiempo real.
Saludos

Link | Reply
Currently unrated

sergio 2 years, 10 months ago

Hola, gracias por contestar.
Como tú lo explicas, si funciona cuando sabes los valores de hora de inicio de cada motor; pero en realidad estós motores se arrancan, cuando el usuario se le ocurra y pueden ser a diferentes tiempos o todos al mismo tiempo.
Tambieén había pensado algo parecido a tú idea
solo que no conozco o mejor dicho no se como tomar la variable (tiempo) del sistema y agregarle los minutos deseados, y despues comparar esta variable contra el reloj del sistema.
Aunque, si estarás de acuerdo que el temporizado, si debe ser bajo un esquema con hilos, cierto?

Link | Reply
Currently unrated

javier 2 years, 10 months ago

Hola,

1. Podrías poner interruptores para encender esos motores y que manden a su vez señal a tu programa en python/arduino o raspberry pi.

2. Te voy a detallar algo el pseudocódigo a ver si lo ves mas claro. (ver enlace)

https://pastebin.com/Sg1eYxMi

3. NO es necesariamente multihilo el planteamiento del programa.
4. El programa NO está probado. Puede tener algun bug.

Link | Reply
Currently unrated

New Comment

* Please fill all required form field, thanks!