Buscar..


Introducción

Los subprocesos permiten que los programas de Python manejen múltiples funciones a la vez en lugar de ejecutar una secuencia de comandos individualmente. Este tema explica los principios detrás de los hilos y demuestra su uso.

Conceptos básicos de multihilo

Usando el módulo de threading , se puede iniciar un nuevo subproceso de ejecución creando un nuevo threading.Thread y asigne una función para ejecutar:

import threading

def foo():
  print "Hello threading!"

my_thread = threading.Thread(target=foo)

El parámetro de target referencia a la función (u objeto llamable) que se ejecutará. El subproceso no comenzará la ejecución hasta que se llame al start en el objeto Thread .

Comenzando un hilo

my_thread.start() # prints 'Hello threading!'

Ahora que my_thread ha ejecutado y finalizado, al start nuevo, se producirá un RuntimeError . Si desea ejecutar su hilo como un demonio, pasar el daemon=True kwarg, o configurar my_thread.daemon en True antes de llamar a start() , hace que su Thread ejecute silenciosamente en segundo plano como un demonio.

Unirse a un hilo

En los casos en que divide un trabajo grande en varios pequeños y desea ejecutarlos simultáneamente, pero debe esperar a que todos terminen antes de continuar, Thread.join() es el método que está buscando.

Por ejemplo, supongamos que desea descargar varias páginas de un sitio web y compilarlas en una sola página. Tu harias esto

import requests
from threading import Thread
from queue import Queue

q = Queue(maxsize=20)
def put_page_to_q(page_num):
    q.put(requests.get('http://some-website.com/page_%s.html' % page_num)

def compile(q):
    # magic function that needs all pages before being able to be executed
    if not q.full():
        raise ValueError
    else:
        print("Done compiling!")

threads = []
for page_num in range(20):
     t = Thread(target=requests.get, args=(page_num,))
     t.start()
     threads.append(t)

# Next, join all threads to make sure all threads are done running before
# we continue. join() is a blocking call (unless specified otherwise using 
# the kwarg blocking=False when calling join)
for t in threads:
    t.join()

# Call compile() now, since all threads have completed
compile(q)

Una mirada más cercana a cómo funciona join() se puede encontrar aquí .

Crear una clase de hilo personalizado

Usando la clase threading.Thread podemos subclasificar la nueva clase Thread personalizada. debemos anular el método de run en una subclase.

from threading import Thread
import time

class Sleepy(Thread):

    def run(self):
        time.sleep(5)
        print("Hello form Thread")

if __name__ == "__main__":
    t = Sleepy()
    t.start()      # start method automatic call Thread class run method.
    # print 'The main program continues to run in foreground.'
    t.join()
    print("The main program continues to run in the foreground.")

Comunicando entre hilos

Hay múltiples hilos en su código y necesita comunicarse de forma segura entre ellos.

Puede utilizar una Queue de la biblioteca de queue .

from queue import Queue
from threading import Thread

# create a data producer 
def producer(output_queue):
    while True:
        data = data_computation()
        
        output_queue.put(data)

# create a consumer
def consumer(input_queue):
    while True:
        # retrieve data (blocking)
        data = input_queue.get()

        # do something with the data

        # indicate data has been consumed
        input_queue.task_done()

Creación de subprocesos de productor y consumidor con una cola compartida

q = Queue()
t1 = Thread(target=consumer, args=(q,))
t2 = Thread(target=producer, args=(q,))
t1.start()
t2.start()

Creando un grupo de trabajadores

Usando threading y queue :

from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread
from queue import Queue
    
def echo_server(addr, nworkers):
    print('Echo server running at', addr)
    # Launch the client workers
    q = Queue()
    for n in range(nworkers):
        t = Thread(target=echo_client, args=(q,))
        t.daemon = True
        t.start()

    # Run the server
    sock = socket(AF_INET, SOCK_STREAM)
    sock.bind(addr)
    sock.listen(5)
    while True:
        client_sock, client_addr = sock.accept()
        q.put((client_sock, client_addr))

echo_server(('',15000), 128)

Utilizando concurrent.futures.Threadpoolexecutor :

from socket import AF_INET, SOCK_STREAM, socket
from concurrent.futures import ThreadPoolExecutor

def echo_server(addr):
    print('Echo server running at', addr)
    pool = ThreadPoolExecutor(128)
    sock = socket(AF_INET, SOCK_STREAM)
    sock.bind(addr)
    sock.listen(5)
    while True:
        client_sock, client_addr = sock.accept()
        pool.submit(echo_client, client_sock, client_addr)

echo_server(('',15000))

Python Cookbook, 3ra edición, por David Beazley y Brian K. Jones (O'Reilly). Derechos de autor 2013 David Beazley y Brian Jones, 978-1-449-34037-7.

Uso avanzado de multihilos

Esta sección contendrá algunos de los ejemplos más avanzados realizados utilizando Multithreading.

Impresora avanzada (logger)

Un hilo que imprime todo se recibe y modifica la salida de acuerdo con el ancho del terminal. Lo bueno es que también la salida "ya escrita" se modifica cuando cambia el ancho del terminal.

#!/usr/bin/env python2

import threading
import Queue
import time
import sys
import subprocess
from backports.shutil_get_terminal_size import get_terminal_size

printq = Queue.Queue()
interrupt = False
lines = []

def main():

    ptt = threading.Thread(target=printer) # Turn the printer on
    ptt.daemon = True
    ptt.start()

    # Stupid example of stuff to print
    for i in xrange(1,100):
        printq.put(' '.join([str(x) for x in range(1,i)]))           # The actual way to send stuff to the printer
        time.sleep(.5)

def split_line(line, cols):
    if len(line) > cols:
        new_line = ''
        ww = line.split()
        i = 0
        while len(new_line) <= (cols - len(ww[i]) - 1):
            new_line += ww[i] + ' '
            i += 1
            print len(new_line)
        if new_line == '':
            return (line, '')

        return (new_line, ' '.join(ww[i:]))
    else:
        return (line, '')


def printer():

    while True:
        cols, rows = get_terminal_size() # Get the terminal dimensions
        msg = '#' + '-' * (cols - 2) + '#\n' # Create the
        try:
            new_line = str(printq.get_nowait())
            if new_line != '!@#EXIT#@!': # A nice way to turn the printer
                                         # thread out gracefully
                lines.append(new_line)
                printq.task_done()
            else:
                printq.task_done()
                sys.exit()
        except Queue.Empty:
            pass

        # Build the new message to show and split too long lines
        for line in lines:
            res = line          # The following is to split lines which are
                                # longer than cols.
            while len(res) !=0:
                toprint, res = split_line(res, cols)
                msg += '\n' + toprint

        # Clear the shell and print the new output
        subprocess.check_call('clear') # Keep the shell clean
        sys.stdout.write(msg)
        sys.stdout.flush()
        time.sleep(.5)

Hilo que se puede detener con un bucle de tiempo

import threading
import time

class StoppableThread(threading.Thread):
    """Thread class with a stop() method. The thread itself has to check
    regularly for the stopped() condition."""

    def __init__(self):
        super(StoppableThread, self).__init__()
        self._stop_event = threading.Event()

    def stop(self):
        self._stop_event.set()

    def join(self, *args, **kwargs):
        self.stop()
        super(StoppableThread,self).join(*args, **kwargs)

    def run()
        while not self._stop_event.is_set():
            print("Still running!")
            time.sleep(2)
        print("stopped!"

Basado en esta pregunta .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow