viernes, 27 de marzo de 2020

MONITOREO DE MULTIPLES HILOS EN PYTHON 3


En este programa se ejemplifica cómo monitorear varios hilos y mostrar el progreso de cada uno en la interfase gráfica. Para saber mas sobre hilos, procesos y demás. puedes leer el post del link.


El programa utiliza unas barras de progreso (gauge) para representar cada hilo en la interfase gráfica. Cada hilo tiene un proceso, el cual consiste en contar de 0 a 100, con un retraso aleatorio de tiempo de entre 5 y 10 mili segundos en cada iteración para simular la variación de tiempos de proceso. Puedes  cambiar el numero de procesos en la variable num_procesos.  Corre este programa una vez y observa como las barras de progreso se llenan casi al mismo tiempo.

semáforo desactivado

En el código mostrado mas abajo hay 2 funciones comentadas. Estas funciones son threadLock.acquire() y threadLock.release(). Estas funciones funcionan (valga la redunancia) como un semáforo que pausa un hilo determinado mientras otro continua. Esto es útil para evitar corromper datos cuando 2 o mas hilos intentan acceder a la misma información para lectura o escritura al mismo tiempo. Des-comenta estas 2 funciones como experimento para observar como las barras de progreso se pausan entre si de modo que ya no se llenan de de forma uniforme.

Semáforo activado

En el código fuente te dejo comentarios comprender lo que sucede. Nesecitas la librería wxpython para correr este programa. Este programa fué probado en Python 3.7.


import wx
from time            import sleep
from random          import randint
from threading       import Thread
from threading       import Lock
from threading       import current_thread
from wx.lib.newevent import NewEvent
 
threadLock = Lock() # descomenta threadLock.acquire() y threadLock.release()
                    # (mas abajo) como experimento para ver que sucede.
MandaGaugeDatos, EVT_ACTUAL_GAUG = NewEvent() # evento que se usara para monitorear hilos y actualizar gauges
num_procesos = 5   # numero de hlos (y por ende, de gauges) pon aqui los que quieras

class TestThread(Thread): # Esto es un hilo.
    def __init__(self,parent,threadID):
        Thread.__init__(self)     # llamar al constructor de la clase
        self.threadID = threadID  # variable para identificar el hilo
        self.parent   = parent    # padre de el hilo (se usa para matar hilo cunando se sierre el programa)

    def run(self): # EL PROCESO
        #--------------------------------------------------
        #   El roceso consiste en contar de 1 a 100.
        #   se generan retardos aleatorios para simular tiempos variantes
        #--------------------------------------------------
        for i in range(1,100): # Cuenta de 1 a 100%
            
            #threadLock.acquire() # DESCOMENTA ESTO A VER QUE PASA.

            slp = randint(5, 10)/1000  # generar tiempo aleatorio entre 5 y 10ms
            sleep(slp)                 # realizar el retraso aleatorio
            #-------------------------------------------------------------
            # Enviar datos resultantes de cada iteracion del hilo a la GUI
            #  wx.postevent(parent, function) #generar evento
            #  MandaGaugeDatos(datos nombrados que quieras regresar)
            #--------------------------------------------------
            wx.PostEvent(self.parent,
                         MandaGaugeDatos(dato   = i,
                                         gauge  = self.threadID,
                                         thread = current_thread()))
            
            #threadLock.release() # DESCOMENTA ESTO A VER QUE PASA.
     
class MainFrame(wx.Frame):
    def __init__(self, parent, title =""):
        super().__init__(parent, title = title , size = (500,250))
        self.panel  = wx.Panel(self)
        
        self.gauges = []  # Arreglo de Gauges
        threads     = []  # arreglo de hilos
        #---------------------------------------------
        #                WIDGETS
        #---------------------------------------------
        #       llenar el arreglo de gauges
        #--------------------------------------------
        for gauge in range(num_procesos):
            self.gauges.append(wx.Gauge(self.panel, size=(300, 20)))
        #---------------------------------------------
        #                 BINDS
        #---------------------------------------------
        # Conectar señal del evento emitida con la funcion correspondiente
        #--------------------------------------------
        self.Bind(EVT_ACTUAL_GAUG, self.on_actualizar)
        #---------------------------------------------
        #                 LAYOUT
        #---------------------------------------------
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(self.panel,1, wx.CENTER|wx.ALL|wx.EXPAND)
        panel_sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel.SetSizer(panel_sizer)
        for gauge in self.gauges:
            panel_sizer.Add(gauge , 0, wx.CENTER|wx.ALL|wx.EXPAND)
        self.SetSizer(main_sizer)
        #---------------------------------------------
        #  llenar arreglo de hilos e  Iniciarlos todos
        #---------------------------------------------
        for x in range(num_procesos):
            threads.append(TestThread(self, x))
            threads[x].daemon = True
            threads[x].start()
            
    def on_actualizar(self, evt):
        #Actualizar el gauge correspondiente en la GUI
        self.gauges[evt.gauge].SetValue(evt.dato)
 
if __name__ == '__main__':
    app = wx.App(0)
    frame = MainFrame(None)
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()

No hay comentarios.:

Publicar un comentario