此代码(snippet_1)改编自 ThreadPoolExecutor Example in doc
import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/']
# 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]
print('%r page is %d bytes' % (url, len(data)))
print('after')
效果很好,并且得到
'http://www.foxnews.com/' page is 990869 bytes 'http://www.cnn.com/' page is 990869 bytes 'http://www.bbc.co.uk/' page is 990869 bytes 'http://europe.wsj.com/' page is 990869 bytes after
此代码是我自己的代码 (snippet_2),用于通过直接函数调用实现相同的工作。
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/']
for url in URLS:
with urllib.request.urlopen(url, timeout=60) as conn:
print('%r page is %d bytes' % (url, len(data)))
print('after')
snippet_1 似乎更常见,但为什么呢?
最佳答案
当您从网络读取内容时,您的应用程序可能会花费大部分时间等待回复。
通常,CPython(您可能正在使用的 Python 实现)内的全局解释器锁可确保一次只有一个线程正在执行 Python 字节码。
但是当等待 I/O(包括网络 I/O)时,GIL 被释放,为其他线程提供运行的机会。这意味着多个读取有效地并行运行,而不是一个接一个地运行,从而缩短了整体执行时间。
对于少数 URI 来说,这不会产生太大影响。但是您使用的 URI 越多,它就越引人注目。
因此,ThreadPoolExecutor
主要用于并行运行 I/O 操作。另一方面,ProcessPoolExecutor 对于并行运行 CPU 密集型任务非常有用。由于它使用多个进程,因此 GIL 的限制不适用。
关于python - 为什么人们会使用 ThreadPoolExecutor 而不是直接函数调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57537215/