我试图了解 ThreadPoolExecutor 和 ProcessPoolExecutors 是如何工作的。我对此测试的假设是,CPU 密集型任务(例如增加计数器)不会从在 ThreadPoolExecutors 上运行中受益,因为它不会释放 GIL,因此它一次只能使用一个进程。
@measure_execution_time
def cpu_slow_function(item):
start = time.time()
duration = random()
counter = 1
while time.time() - start < duration:
counter += 1
return item, counter
def test_thread_pool__cpu_bound():
"""
100 tasks of average .5 seconds each, would take 50 seconds to complete sequentially.
"""
items = list(range(100))
with ThreadPoolExecutor(max_workers=100) as executor:
results = list(executor.map(cpu_slow_function, items))
for index, (result, counter) in enumerate(results):
assert result == index
assert counter >= 0.0
令我惊讶的是,这个测试大约需要 5 秒才能完成。根据我的假设,应该需要大约 50 秒,100 个任务,每个平均 0.5 秒。
我错过了什么?
GIL 不会阻止 Python 线程并发运行。它只会阻止多个线程在任何给定时刻执行字节代码。
在任何给定时刻,您的程序都会有一个实际上正在执行字节代码的工作人员,以及 99 个工作人员都在等待执行下一个字节代码的机会,同时,
time.time()
对于所有来说,时钟正在滴答作响(即,实时正在流逝)。