Ricerca…


introduzione

I thread consentono ai programmi Python di gestire più funzioni contemporaneamente anziché eseguire una sequenza di comandi individualmente. Questo argomento spiega i principi alla base del threading e ne dimostra l'utilizzo.

Nozioni di base sul multithreading

Utilizzando il modulo di threading , è possibile avviare un nuovo thread di esecuzione creando un nuovo threading.Thread e assegnargli una funzione da eseguire:

import threading

def foo():
  print "Hello threading!"

my_thread = threading.Thread(target=foo)

Il parametro target riferimento alla funzione (o oggetto callable) da eseguire. Il thread non inizierà l'esecuzione finché non viene chiamato start sull'oggetto Thread .

Iniziare una discussione

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

Ora che my_thread è stato eseguito e terminato, richiamando start verrà RuntimeError un RuntimeError . Se si desidera eseguire il thread come daemon, passando il daemon=True kwarg o impostando my_thread.daemon su True prima di chiamare start() , il Thread avvia silenziosamente in background come daemon.

Partecipare a una discussione

Nei casi in cui dividi un grosso lavoro in più di uno piccolo e desideri eseguirli contemporaneamente, ma Thread.join() aspettare che finiscano tutti prima di continuare, Thread.join() è il metodo che stai cercando.

Ad esempio, supponiamo di voler scaricare diverse pagine di un sito Web e di compilarle in un'unica pagina. Faresti questo:

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)

Uno sguardo più ravvicinato a come funziona join() può essere trovato qui .

Creare una classe thread personalizzata

Usando la classe threading.Thread possiamo sottoclasse la nuova classe Thread personalizzata. dobbiamo sovrascrivere il metodo run in una sottoclasse.

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.")

Comunicare tra i thread

Ci sono più thread nel codice e devi comunicare in sicurezza tra di loro.

È possibile utilizzare una Queue dalla libreria della 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()

Creazione di thread di produzione e di consumo con una coda condivisa

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

Creazione di un pool di worker

Utilizzo di threading e 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)

Utilizzando 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, 3rd edition, di David Beazley e Brian K. Jones (O'Reilly). Copyright 2013 David Beazley e Brian Jones, 978-1-449-34037-7.

Uso avanzato di multithread

Questa sezione conterrà alcuni degli esempi più avanzati realizzati utilizzando il multithreading.

Stampante avanzata (logger)

Un thread che stampa tutto viene ricevuto e modifica l'output in base alla larghezza del terminale. La parte interessante è che anche l'output "già scritto" viene modificato quando cambia la larghezza del terminale.

#!/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)

Filo bloccabile con un ciclo while

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!"

Basato su questa domanda .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow