Sto cercando di capire il threading in Python. Ho esaminato la documentazione e gli esempi, ma francamente, molti esempi sono eccessivamente sofisticati e non riesco a capirli.
Come si mostrano chiaramente le attività divise per il multi-threading?
Sto cercando di capire il threading in Python. Ho esaminato la documentazione e gli esempi, ma francamente, molti esempi sono eccessivamente sofisticati e non riesco a capirli.
Come si mostrano chiaramente le attività divise per il multi-threading?
Risposte:
Da quando questa domanda è stata posta nel 2010, c'è stata una vera semplificazione nel modo di fare il multithreading semplice con Python con mappa e pool .
Il codice seguente proviene da un articolo / post sul blog che dovresti assolutamente provare (nessuna affiliazione) - Parallelismo su una riga: un modello migliore per le attività di threading giornaliere . Riassumo di seguito: finisce per essere solo poche righe di codice:
from multiprocessing.dummy import Pool as ThreadPool
pool = ThreadPool(4)
results = pool.map(my_function, my_array)
Qual è la versione multithread di:
results = []
for item in my_array:
results.append(my_function(item))
Descrizione
Map è una piccola funzione interessante e la chiave per iniettare facilmente parallelismo nel tuo codice Python. Per coloro che non hanno familiarità, la mappa è qualcosa di sollevato da linguaggi funzionali come Lisp. È una funzione che mappa un'altra funzione su una sequenza.
Map gestisce l'iterazione sulla sequenza per noi, applica la funzione e memorizza tutti i risultati in un pratico elenco alla fine.
Implementazione
Le versioni parallele della funzione mappa sono fornite da due librerie: multiprocessing, e anche il suo step step poco conosciuto, ma altrettanto fantastico: multiprocessing.dummy.
multiprocessing.dummy
è esattamente lo stesso del modulo multiprocessore, ma utilizza invece i thread ( una distinzione importante : utilizzare più processi per attività ad alta intensità di CPU; thread per (e durante) l'I / O ):
multiprocessing.dummy replica l'API del multiprocessing, ma non è altro che un wrapper attorno al modulo threading.
import urllib2
from multiprocessing.dummy import Pool as ThreadPool
urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
'http://www.python.org/doc/',
'http://www.python.org/download/',
'http://www.python.org/getit/',
'http://www.python.org/community/',
'https://wiki.python.org/moin/',
]
# Make the Pool of workers
pool = ThreadPool(4)
# Open the URLs in their own threads
# and return the results
results = pool.map(urllib2.urlopen, urls)
# Close the pool and wait for the work to finish
pool.close()
pool.join()
E i risultati dei tempi:
Single thread: 14.4 seconds
4 Pool: 3.1 seconds
8 Pool: 1.4 seconds
13 Pool: 1.3 seconds
Passando più argomenti (funziona così solo in Python 3.3 e versioni successive ):
Per passare più matrici:
results = pool.starmap(function, zip(list_a, list_b))
O per passare una costante e un array:
results = pool.starmap(function, zip(itertools.repeat(constant), list_a))
Se si utilizza una versione precedente di Python, è possibile passare più argomenti tramite questa soluzione alternativa ).
(Grazie a user136036 per il commento utile.)
with Pool(8) as p: p.map( *whatever* )
e sbarazzarvi anche delle righe di contabilità.
Ecco un semplice esempio: devi provare alcuni URL alternativi e restituire il contenuto del primo per rispondere.
import Queue
import threading
import urllib2
# Called by each thread
def get_url(q, url):
q.put(urllib2.urlopen(url).read())
theurls = ["http://google.com", "http://yahoo.com"]
q = Queue.Queue()
for u in theurls:
t = threading.Thread(target=get_url, args = (q,u))
t.daemon = True
t.start()
s = q.get()
print s
Questo è un caso in cui il threading viene utilizzato come una semplice ottimizzazione: ogni sottotread è in attesa che un URL si risolva e risponda, al fine di mettere i suoi contenuti in coda; ogni thread è un demone (non continuerà il processo se il thread principale termina - è più comune che no); il thread principale avvia tutti i sottotread, fa un get
in coda per aspettare fino a quando uno di loro ha fatto unput
, quindi emette i risultati e termina (che elimina tutti i sottotread che potrebbero essere ancora in esecuzione, poiché sono thread daemon).
L'uso corretto dei thread in Python è invariabilmente collegato alle operazioni di I / O (poiché CPython non utilizza più core per eseguire comunque attività legate alla CPU, l'unico motivo per il threading non è bloccare il processo mentre si attende qualche I / O ). Le code sono quasi invariabilmente il modo migliore per distribuire il lavoro sui thread e / o raccogliere i risultati del lavoro, a proposito, e sono intrinsecamente sicuri per il thread, quindi ti evitano di preoccuparti di blocchi, condizioni, eventi, semafori e altri inter concetti di coordinamento / comunicazione.
join()
metodo, dal momento che farebbe attendere il thread principale fino a quando non saranno completati senza consumare il processore costantemente verifica del valore. @Alex: grazie, questo è esattamente ciò di cui avevo bisogno per capire come usare i thread.
Queue
nome del modulo con queue
. Il nome del metodo è lo stesso.
s = q.get()
print s
@ krs013 Non è necessario join
perché Queue.get () sta bloccando.
NOTA : per l'effettiva parallelizzazione in Python, è necessario utilizzare il multiprocessing modulo per fork più processi che vengono eseguiti in parallelo (a causa del blocco dell'interprete globale, i thread Python forniscono interleaving, ma in realtà vengono eseguiti in serie, non in parallelo, e sono solo utile durante le operazioni di I / O interleaving).
Tuttavia, se stai semplicemente cercando interleaving (o stai eseguendo operazioni di I / O che possono essere parallelizzate nonostante il blocco dell'interprete globale), il modulo di threading è il punto di partenza. A titolo di esempio molto semplice, consideriamo il problema di sommare un ampio intervallo sommando subrange in parallelo:
import threading
class SummingThread(threading.Thread):
def __init__(self,low,high):
super(SummingThread, self).__init__()
self.low=low
self.high=high
self.total=0
def run(self):
for i in range(self.low,self.high):
self.total+=i
thread1 = SummingThread(0,500000)
thread2 = SummingThread(500000,1000000)
thread1.start() # This actually causes the thread to run
thread2.start()
thread1.join() # This waits until the thread has completed
thread2.join()
# At this point, both threads have completed
result = thread1.total + thread2.total
print result
Si noti che quanto sopra è un esempio molto stupido, in quanto non ha assolutamente I / O e verrà eseguito in serie sebbene interfogliato (con l'overhead aggiuntivo del cambio di contesto) in CPython a causa del blocco dell'interprete globale.
thread1
viene eseguito fino a quando non viene completato mentre il thread principale si blocca, quindi accade la stessa cosa thread2
, quindi il thread principale riprende e stampa i valori accumulati.
super(SummingThread, self).__init__()
? Come in stackoverflow.com/a/2197625/806988
Come altri menzionati, CPython può usare i thread solo per le attese di I / O dovute a GIL .
Se si desidera beneficiare di più core per attività associate alla CPU, utilizzare il multiprocessing :
from multiprocessing import Process
def f(name):
print 'hello', name
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()
f
funzione. Parallelamente, il programma principale ora aspetta solo che il processo termini, join
finendo con esso. Se la parte principale è appena uscita, il sottoprocesso potrebbe o non potrebbe essere eseguito fino al completamento, pertanto join
è sempre consigliabile eseguire una .
map
funzione è qui: stackoverflow.com/a/28463266/2327328
Solo una nota: non è richiesta una coda per il threading.
Questo è l'esempio più semplice che potrei immaginare che mostra 10 processi in esecuzione contemporaneamente.
import threading
from random import randint
from time import sleep
def print_number(number):
# Sleeps a random 1 to 10 seconds
rand_int_var = randint(1, 10)
sleep(rand_int_var)
print "Thread " + str(number) + " slept for " + str(rand_int_var) + " seconds"
thread_list = []
for i in range(1, 10):
# Instantiates the thread
# (i) does not make a sequence, so (i,)
t = threading.Thread(target=print_number, args=(i,))
# Sticks the thread in a list so that it remains accessible
thread_list.append(t)
# Starts threads
for thread in thread_list:
thread.start()
# This blocks the calling thread until the thread whose join() method is called is terminated.
# From http://docs.python.org/2/library/threading.html#thread-objects
for thread in thread_list:
thread.join()
# Demonstrates that the main process waited for threads to complete
print "Done"
for
loop, è possibile chiamare thread.start()
nel primo loop.
La risposta di Alex Martelli mi ha aiutato. Tuttavia, ecco una versione modificata che pensavo fosse più utile (almeno per me).
Aggiornato: funziona sia in Python 2 che in Python 3
try:
# For Python 3
import queue
from urllib.request import urlopen
except:
# For Python 2
import Queue as queue
from urllib2 import urlopen
import threading
worker_data = ['http://google.com', 'http://yahoo.com', 'http://bing.com']
# Load up a queue with your data. This will handle locking
q = queue.Queue()
for url in worker_data:
q.put(url)
# Define a worker function
def worker(url_queue):
queue_full = True
while queue_full:
try:
# Get your data off the queue, and do some work
url = url_queue.get(False)
data = urlopen(url).read()
print(len(data))
except queue.Empty:
queue_full = False
# Create as many threads as you want
thread_count = 5
for i in range(thread_count):
t = threading.Thread(target=worker, args = (q,))
t.start()
import Queue ModuleNotFoundError: No module named 'Queue'
sto eseguendo python 3.6.5 alcuni post menzionano che in python 3.6.5 lo è, queue
ma anche dopo che lo cambio, non funziona ancora
Data una funzione, f
esegui il thread in questo modo:
import threading
threading.Thread(target=f).start()
Per passare argomenti a f
threading.Thread(target=f, args=(a,b,c)).start()
Thread
oggetto pulisce. Vedi i documenti . C'è un is_alive()
metodo che puoi usare per controllare un thread, se necessario.
is_alive
metodo, ma non sono riuscito a capire come applicarlo al thread. Ho provato ad assegnare thread1=threading.Thread(target=f).start()
e quindi a verificarlo thread1.is_alive()
, ma thread1
è popolato con None
, quindi non ho avuto fortuna. Sai se esiste un altro modo per accedere al thread?
thread1=threading.Thread(target=f)
seguito da thread1.start()
. Quindi puoi farlo thread1.is_alive()
.
thread1.is_alive()
ritorni False
non appena la funzione esce.
L'ho trovato molto utile: crea tanti thread quanti core e lascia che eseguano un numero (elevato) di compiti (in questo caso, chiamando un programma shell):
import Queue
import threading
import multiprocessing
import subprocess
q = Queue.Queue()
for i in range(30): # Put 30 tasks in the queue
q.put(i)
def worker():
while True:
item = q.get()
# Execute a task: call a shell program and wait until it completes
subprocess.call("echo " + str(item), shell=True)
q.task_done()
cpus = multiprocessing.cpu_count() # Detect number of cores
print("Creating %d threads" % cpus)
for i in range(cpus):
t = threading.Thread(target=worker)
t.daemon = True
t.start()
q.join() # Block until all tasks are done
Python 3 ha la possibilità di avviare attività parallele . Questo semplifica il nostro lavoro.
Dispone di pool di thread e pool di processi .
Di seguito viene fornita una panoramica:
Esempio ThreadPoolExecutor ( sorgente )
import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
with urllib.request.urlopen(url, timeout=timeout) as conn:
return conn.read()
# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# Start the load operations and mark each future with its URL
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))
ProcessPoolExecutor ( fonte )
import concurrent.futures
import math
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
def is_prime(n):
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
main()
Utilizzando il nuovo ardente modulo concurrent.futures
def sqr(val):
import time
time.sleep(0.1)
return val * val
def process_result(result):
print(result)
def process_these_asap(tasks):
import concurrent.futures
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = []
for task in tasks:
futures.append(executor.submit(sqr, task))
for future in concurrent.futures.as_completed(futures):
process_result(future.result())
# Or instead of all this just do:
# results = executor.map(sqr, tasks)
# list(map(process_result, results))
def main():
tasks = list(range(10))
print('Processing {} tasks'.format(len(tasks)))
process_these_asap(tasks)
print('Done')
return 0
if __name__ == '__main__':
import sys
sys.exit(main())
L'approccio degli esecutori potrebbe sembrare familiare a tutti coloro che prima si sono sporcati le mani con Java.
Anche su una nota a margine: per mantenere l'universo sano, non dimenticare di chiudere i tuoi pool / esecutori se non usi il with
contesto (che è così fantastico che lo fa per te)
Per me, l'esempio perfetto per il threading è il monitoraggio di eventi asincroni. Guarda questo codice.
# thread_test.py
import threading
import time
class Monitor(threading.Thread):
def __init__(self, mon):
threading.Thread.__init__(self)
self.mon = mon
def run(self):
while True:
if self.mon[0] == 2:
print "Mon = 2"
self.mon[0] = 3;
Puoi giocare con questo codice aprendo una sessione IPython e facendo qualcosa del tipo:
>>> from thread_test import Monitor
>>> a = [0]
>>> mon = Monitor(a)
>>> mon.start()
>>> a[0] = 2
Mon = 2
>>>a[0] = 2
Mon = 2
Aspetta qualche minuto
>>> a[0] = 2
Mon = 2
La maggior parte della documentazione e dei tutorial usa Python Threading
eQueue
modulo e potrebbero sembrare travolgenti per i principianti.
Forse considera il concurrent.futures.ThreadPoolExecutor
modulo di Python 3.
Combinato con la with
clausola e la comprensione dell'elenco potrebbe essere un vero fascino.
from concurrent.futures import ThreadPoolExecutor, as_completed
def get_url(url):
# Your actual program here. Using threading.Lock() if necessary
return ""
# List of URLs to fetch
urls = ["url1", "url2"]
with ThreadPoolExecutor(max_workers = 5) as executor:
# Create threads
futures = {executor.submit(get_url, url) for url in urls}
# as_completed() gives you the threads once finished
for f in as_completed(futures):
# Get the results
rs = f.result()
Ho visto molti esempi qui in cui non veniva eseguito alcun lavoro reale, ed erano per lo più legati alla CPU. Ecco un esempio di un'attività associata alla CPU che calcola tutti i numeri primi tra 10 milioni e 10,05 milioni. Ho usato tutti e quattro i metodi qui:
import math
import timeit
import threading
import multiprocessing
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def time_stuff(fn):
"""
Measure time of execution of a function
"""
def wrapper(*args, **kwargs):
t0 = timeit.default_timer()
fn(*args, **kwargs)
t1 = timeit.default_timer()
print("{} seconds".format(t1 - t0))
return wrapper
def find_primes_in(nmin, nmax):
"""
Compute a list of prime numbers between the given minimum and maximum arguments
"""
primes = []
# Loop from minimum to maximum
for current in range(nmin, nmax + 1):
# Take the square root of the current number
sqrt_n = int(math.sqrt(current))
found = False
# Check if the any number from 2 to the square root + 1 divides the current numnber under consideration
for number in range(2, sqrt_n + 1):
# If divisible we have found a factor, hence this is not a prime number, lets move to the next one
if current % number == 0:
found = True
break
# If not divisible, add this number to the list of primes that we have found so far
if not found:
primes.append(current)
# I am merely printing the length of the array containing all the primes, but feel free to do what you want
print(len(primes))
@time_stuff
def sequential_prime_finder(nmin, nmax):
"""
Use the main process and main thread to compute everything in this case
"""
find_primes_in(nmin, nmax)
@time_stuff
def threading_prime_finder(nmin, nmax):
"""
If the minimum is 1000 and the maximum is 2000 and we have four workers,
1000 - 1250 to worker 1
1250 - 1500 to worker 2
1500 - 1750 to worker 3
1750 - 2000 to worker 4
so let’s split the minimum and maximum values according to the number of workers
"""
nrange = nmax - nmin
threads = []
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
# Start the thread with the minimum and maximum split up to compute
# Parallel computation will not work here due to the GIL since this is a CPU-bound task
t = threading.Thread(target = find_primes_in, args = (start, end))
threads.append(t)
t.start()
# Don’t forget to wait for the threads to finish
for t in threads:
t.join()
@time_stuff
def processing_prime_finder(nmin, nmax):
"""
Split the minimum, maximum interval similar to the threading method above, but use processes this time
"""
nrange = nmax - nmin
processes = []
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
p = multiprocessing.Process(target = find_primes_in, args = (start, end))
processes.append(p)
p.start()
for p in processes:
p.join()
@time_stuff
def thread_executor_prime_finder(nmin, nmax):
"""
Split the min max interval similar to the threading method, but use a thread pool executor this time.
This method is slightly faster than using pure threading as the pools manage threads more efficiently.
This method is still slow due to the GIL limitations since we are doing a CPU-bound task.
"""
nrange = nmax - nmin
with ThreadPoolExecutor(max_workers = 8) as e:
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
e.submit(find_primes_in, start, end)
@time_stuff
def process_executor_prime_finder(nmin, nmax):
"""
Split the min max interval similar to the threading method, but use the process pool executor.
This is the fastest method recorded so far as it manages process efficiently + overcomes GIL limitations.
RECOMMENDED METHOD FOR CPU-BOUND TASKS
"""
nrange = nmax - nmin
with ProcessPoolExecutor(max_workers = 8) as e:
for i in range(8):
start = int(nmin + i * nrange/8)
end = int(nmin + (i + 1) * nrange/8)
e.submit(find_primes_in, start, end)
def main():
nmin = int(1e7)
nmax = int(1.05e7)
print("Sequential Prime Finder Starting")
sequential_prime_finder(nmin, nmax)
print("Threading Prime Finder Starting")
threading_prime_finder(nmin, nmax)
print("Processing Prime Finder Starting")
processing_prime_finder(nmin, nmax)
print("Thread Executor Prime Finder Starting")
thread_executor_prime_finder(nmin, nmax)
print("Process Executor Finder Starting")
process_executor_prime_finder(nmin, nmax)
main()
Ecco i risultati sul mio computer a quattro core Mac OS X.
Sequential Prime Finder Starting
9.708213827005238 seconds
Threading Prime Finder Starting
9.81836523200036 seconds
Processing Prime Finder Starting
3.2467174359990167 seconds
Thread Executor Prime Finder Starting
10.228896902000997 seconds
Process Executor Finder Starting
2.656402041000547 seconds
if __name__ == '__main__':
prima della chiamata principale, altrimenti la misura stessa e stampe spawn Un tentativo è stato fatto per avviare un nuovo processo prima ... .
Ecco l'esempio molto semplice dell'importazione CSV usando il threading. (L'inclusione della biblioteca può differire per scopi diversi.)
Funzioni di supporto:
from threading import Thread
from project import app
import csv
def import_handler(csv_file_name):
thr = Thread(target=dump_async_csv_data, args=[csv_file_name])
thr.start()
def dump_async_csv_data(csv_file_name):
with app.app_context():
with open(csv_file_name) as File:
reader = csv.DictReader(File)
for row in reader:
# DB operation/query
Funzione conducente:
import_handler(csv_file_name)
Vorrei contribuire con un semplice esempio e le spiegazioni che ho trovato utili quando ho dovuto affrontare questo problema da solo.
In questa risposta troverai alcune informazioni su GIL (blocco dell'interprete globale) di Python e un semplice esempio quotidiano scritto usando multiprocessing.dummy oltre ad alcuni semplici benchmark.
Global Interpreter Lock (GIL)
Python non consente il multi-threading nel vero senso della parola. Ha un pacchetto multi-thread, ma se vuoi multi-thread per velocizzare il tuo codice, di solito non è una buona idea usarlo.
Python ha un costrutto chiamato global interpreter lock (GIL). GIL si assicura che solo uno dei tuoi "thread" possa essere eseguito contemporaneamente. Un thread acquisisce il GIL, fa un po 'di lavoro, quindi passa il GIL al thread successivo.
Questo accade molto rapidamente, quindi all'occhio umano può sembrare che i tuoi thread si stiano eseguendo in parallelo, ma in realtà stanno facendo a turno usando lo stesso core della CPU.
Tutto questo passaggio GIL aggiunge sovraccarico all'esecuzione. Ciò significa che se si desidera rendere più veloce l'esecuzione del codice, l'utilizzo del pacchetto di threading spesso non è una buona idea.
Ci sono ragioni per usare il pacchetto di threading di Python. Se vuoi eseguire alcune cose contemporaneamente e l'efficienza non è un problema, allora è totalmente a posto e conveniente. O se stai eseguendo un codice che deve attendere qualcosa (come alcuni I / O), potrebbe avere molto senso. Ma la libreria di threading non ti permetterà di usare core di CPU extra.
Il multi-threading può essere esternalizzato al sistema operativo (eseguendo l'elaborazione multipla) e alcune applicazioni esterne che chiamano il tuo codice Python (ad esempio Spark o Hadoop ) o un codice che chiama il tuo codice Python (ad esempio: potresti chiedi al tuo codice Python di chiamare una funzione C che fa le costose cose multi-thread).
Perché questo conta
Perché molte persone passano molto tempo a cercare colli di bottiglia nel loro fantastico codice multi-thread di Python prima di scoprire cos'è il GIL.
Una volta che queste informazioni sono chiare, ecco il mio codice:
#!/bin/python
from multiprocessing.dummy import Pool
from subprocess import PIPE,Popen
import time
import os
# In the variable pool_size we define the "parallelness".
# For CPU-bound tasks, it doesn't make sense to create more Pool processes
# than you have cores to run them on.
#
# On the other hand, if you are using I/O-bound tasks, it may make sense
# to create a quite a few more Pool processes than cores, since the processes
# will probably spend most their time blocked (waiting for I/O to complete).
pool_size = 8
def do_ping(ip):
if os.name == 'nt':
print ("Using Windows Ping to " + ip)
proc = Popen(['ping', ip], stdout=PIPE)
return proc.communicate()[0]
else:
print ("Using Linux / Unix Ping to " + ip)
proc = Popen(['ping', ip, '-c', '4'], stdout=PIPE)
return proc.communicate()[0]
os.system('cls' if os.name=='nt' else 'clear')
print ("Running using threads\n")
start_time = time.time()
pool = Pool(pool_size)
website_names = ["www.google.com","www.facebook.com","www.pinterest.com","www.microsoft.com"]
result = {}
for website_name in website_names:
result[website_name] = pool.apply_async(do_ping, args=(website_name,))
pool.close()
pool.join()
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Now we do the same without threading, just to compare time
print ("\nRunning NOT using threads\n")
start_time = time.time()
for website_name in website_names:
do_ping(website_name)
print ("\n--- Execution took {} seconds ---".format((time.time() - start_time)))
# Here's one way to print the final output from the threads
output = {}
for key, value in result.items():
output[key] = value.get()
print ("\nOutput aggregated in a Dictionary:")
print (output)
print ("\n")
print ("\nPretty printed output: ")
for key, value in output.items():
print (key + "\n")
print (value)
Ecco il multi threading con un semplice esempio che sarà utile. Puoi eseguirlo e capire facilmente come funziona il multi-threading in Python. Ho usato un lucchetto per impedire l'accesso ad altri thread fino a quando i thread precedenti non hanno completato il loro lavoro. Con l'uso di questa riga di codice,
tLock = threading.BoundedSemaphore (valore = 4)
puoi consentire un numero di processi alla volta e mantenere il resto dei thread che verranno eseguiti in seguito o dopo aver terminato i processi precedenti.
import threading
import time
#tLock = threading.Lock()
tLock = threading.BoundedSemaphore(value=4)
def timer(name, delay, repeat):
print "\r\nTimer: ", name, " Started"
tLock.acquire()
print "\r\n", name, " has the acquired the lock"
while repeat > 0:
time.sleep(delay)
print "\r\n", name, ": ", str(time.ctime(time.time()))
repeat -= 1
print "\r\n", name, " is releaseing the lock"
tLock.release()
print "\r\nTimer: ", name, " Completed"
def Main():
t1 = threading.Thread(target=timer, args=("Timer1", 2, 5))
t2 = threading.Thread(target=timer, args=("Timer2", 3, 5))
t3 = threading.Thread(target=timer, args=("Timer3", 4, 5))
t4 = threading.Thread(target=timer, args=("Timer4", 5, 5))
t5 = threading.Thread(target=timer, args=("Timer5", 0.1, 5))
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
print "\r\nMain Complete"
if __name__ == "__main__":
Main()
Con il prestito da questo post sappiamo di scegliere tra multithreading, multiprocessing e async / asyncio
e il loro utilizzo.
Python 3 ha una nuova libreria integrata per la concorrenza e il parallelismo: concurrent.futures
Quindi, attraverso un esperimento, dimostrerò di eseguire quattro attività (cioè il .sleep()
metodo) nel Threading-Pool
modo seguente:
from concurrent.futures import ThreadPoolExecutor, as_completed
from time import sleep, time
def concurrent(max_worker=1):
futures = []
tick = time()
with ThreadPoolExecutor(max_workers=max_worker) as executor:
futures.append(executor.submit(sleep, 2)) # Two seconds sleep
futures.append(executor.submit(sleep, 1))
futures.append(executor.submit(sleep, 7))
futures.append(executor.submit(sleep, 3))
for future in as_completed(futures):
if future.result() is not None:
print(future.result())
print('Total elapsed time by {} workers:'.format(max_worker), time()-tick)
concurrent(5)
concurrent(4)
concurrent(3)
concurrent(2)
concurrent(1)
Produzione:
Total elapsed time by 5 workers: 7.007831811904907
Total elapsed time by 4 workers: 7.007944107055664
Total elapsed time by 3 workers: 7.003149509429932
Total elapsed time by 2 workers: 8.004627466201782
Total elapsed time by 1 workers: 13.013478994369507
[ NOTA ]:
multiprocessing
vs threading
) è possibile modificare ThreadPoolExecutor
in ProcessPoolExecutor
.Nessuna delle soluzioni precedenti utilizzava più core sul mio server GNU / Linux (dove non ho i diritti di amministratore). Funzionavano su un solo core.
Ho usato l' os.fork
interfaccia di livello inferiore per generare più processi. Questo è il codice che ha funzionato per me:
from os import fork
values = ['different', 'values', 'for', 'threads']
for i in range(len(values)):
p = fork()
if p == 0:
my_function(values[i])
break
import threading
import requests
def send():
r = requests.get('https://www.stackoverlow.com')
thread = []
t = threading.Thread(target=send())
thread.append(t)
t.start()