我想提供一个简单的示例和我发现在我必须自己解决这个问题时有用的解释。
在此答案中,您将找到有关 Python 的 GIL(全局解释器锁)的一些信息以及使用 multiprocessing.dummy 编写的简单日常示例以及一些简单的基准测试。
全局解释器锁 (GIL)
Python 不允许真正意义上的多线程。它有一个多线程包,但是如果你想多线程来加速你的代码,那么使用它通常不是一个好主意。
Python 有一个称为全局解释器锁 (GIL) 的结构。
GIL 确保在任何时候只有一个“线程”可以执行。一个线程获取 GIL,做一些工作,然后将 GIL 传递给下一个线程。
这发生得非常快,因此在人眼看来,您的线程似乎是在并行执行,但实际上它们只是轮流使用相同的 CPU 内核。
所有这些 GIL 传递都会增加执行开销。这意味着如果你想让你的代码运行得更快,那么使用线程
打包通常不是一个好主意。
使用 Python 的线程包是有原因的。如果你想同时运行一些东西,效率不是问题,
然后就完全没问题了,很方便。或者,如果您正在运行需要等待某事(例如某些 I/O)的代码,那么它可能会很有意义。但是线程库不允许您使用额外的 CPU 内核。
多线程可以外包给操作系统(通过执行多处理),以及一些调用您的 Python 代码的外部应用程序(例如,Spark 或 Hadoop),或者您的 Python 代码的一些代码调用(例如:你可以让你的 Python 代码调用一个 C 函数来执行昂贵的多线程操作)。
为什么重要
因为很多人在了解 GIL 是什么之前,会花费大量时间试图找到他们花哨的 Python 多线程代码中的瓶颈。
一旦这些信息清楚,这是我的代码:
#!/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)