Ich möchte mit einem einfachen Beispiel und den Erklärungen, die ich nützlich fand, als ich dieses Problem selbst angehen musste, einen Beitrag leisten.
In dieser Antwort finden Sie einige Informationen zu Pythons GIL (Global Interpreter Lock) und ein einfaches Beispiel aus dem Alltag, das mit multiprocessing.dummy geschrieben wurde, sowie einige einfache Benchmarks.
Global Interpreter Lock (GIL)
Python erlaubt kein Multithreading im wahrsten Sinne des Wortes. Es verfügt über ein Multithreading-Paket. Wenn Sie jedoch ein Multithreading-Paket verwenden möchten, um Ihren Code zu beschleunigen, ist es normalerweise keine gute Idee, es zu verwenden.
Python hat ein Konstrukt namens Global Interpreter Lock (GIL). Die GIL stellt sicher, dass immer nur einer Ihrer 'Threads' gleichzeitig ausgeführt werden kann. Ein Thread erwirbt die GIL, erledigt ein wenig Arbeit und leitet die GIL dann an den nächsten Thread weiter.
Dies geschieht sehr schnell, so dass es für das menschliche Auge so aussieht, als würden Ihre Threads parallel ausgeführt, aber sie wechseln sich nur mit demselben CPU-Kern ab.
All diese GIL-Übergaben erhöhen den Aufwand für die Ausführung. Dies bedeutet, dass die Verwendung des Threading-Pakets häufig keine gute Idee ist, wenn Sie Ihren Code schneller ausführen möchten.
Es gibt Gründe, das Threading-Paket von Python zu verwenden. Wenn Sie einige Dinge gleichzeitig ausführen möchten und Effizienz kein Problem darstellt, ist dies völlig in Ordnung und praktisch. Oder wenn Sie Code ausführen, der auf etwas warten muss (wie z. B. einige E / A), kann dies sehr sinnvoll sein. In der Threading-Bibliothek können Sie jedoch keine zusätzlichen CPU-Kerne verwenden.
Multithreading kann an das Betriebssystem ausgelagert werden (durch Multiverarbeitung) und an eine externe Anwendung, die Ihren Python-Code aufruft (z. B. Spark oder Hadoop ), oder an einen Code, den Ihr Python-Code aufruft (z. B.: Sie könnten Lassen Sie Ihren Python-Code eine C-Funktion aufrufen, die die teuren Multithread-Aufgaben erledigt.
Warum das wichtig ist
Weil viele Leute viel Zeit damit verbringen, Engpässe in ihrem ausgefallenen Python-Multithread-Code zu finden, bevor sie lernen, was die GIL ist.
Sobald diese Informationen klar sind, ist hier mein Code:
#!/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)