关于concurrent.futures.ThreadPoolExecutor递归创建线程的执行逻辑疑问
关于concurrent.futures.ThreadPoolExecutor递归创建线程的执行逻辑疑问
咱们先把你的代码执行流程和背后的逻辑拆解开,你遇到的这些看起来“混乱”的输出,其实都是线程池调度规则+Python的GIL(全局解释器锁)共同作用的结果,我一步步给你捋清楚:
首先明确ThreadPoolExecutor的核心行为
ThreadPoolExecutor的submit()方法会把你的任务扔进线程池的任务队列,只要池里有空闲的worker线程(没超过max_workers上限),就会立刻启动这个任务——不是等当前正在执行的任务跑完,而是直接调度空闲线程去执行新任务。
另外,Python的线程是操作系统级的线程,但因为GIL的存在,同一时刻只有一个线程能执行Python字节码:
- 当线程遇到阻塞操作(比如
time.sleep()、IO请求),会主动释放GIL,让其他线程有机会执行; - 如果线程在执行非阻塞的代码(比如
print、变量运算),会持有GIL直到执行完一定量的字节码,或者被操作系统强制调度切换。
分析你第一个代码的输出差异
没有time.sleep(30)的情况
你初始提交了f(5)到线程池,然后用cf.wait(tasks_list)等待初始任务完成:
- 线程池启动一个线程执行
f(0):打印Task starting for 0,然后进入time.sleep(5)——这时候线程释放GIL,池里还有9个空闲worker。 - 但
cf.wait(tasks_list)只盯着初始的那个future,当f(0)的sleep结束后,它会执行i +=1、提交f(5)(任务1)、打印Done sleeping for 0,这时候初始future完成,cf.wait返回,主线程立刻调用executor.shutdown(wait=True)。 - 虽然
shutdown(wait=True)会等池里所有任务完成,但可能因为stdout的缓冲问题,或者你误以为程序停了就提前终止,导致输出只到Creating new task 2。
加上time.sleep(30)的情况
主线程在cf.wait之后进入time.sleep(30),主动释放GIL,让线程池里的所有任务都有充足的机会被调度执行:
- 每个任务执行到
time.sleep(5)时释放GIL,线程池会立刻启动新提交的任务(只要worker有空),所以你会看到“任务启动→sleep→提交新任务→新任务启动→前一个任务完成打印”这样交替的流程。
解释修改后代码的“异常”输出
你修改后的代码里,f函数的执行顺序是:
def f(x): global i print("Task starting for {}".format(i)) print("***************") time.sleep(x) # 这里会释放GIL if i < 10: i += 1 print("Creating new task {}".format(i)) executor.submit(f, x) # 提交新任务,池里有空worker就立刻启动 print("Done sleeping for {}".format(i-1))
你看到的这段输出:
Task starting for 2
Creating new task 3
Task starting for 3Done sleeping for 2
原因是:
f(2)执行完sleep(5)后,i变成3,打印Creating new task 3,然后submit(f,5)——线程池立刻启动这个新任务f(3)。- 这时候
f(2)还没执行最后的print("Done sleeping for 2"),操作系统的线程调度器把GIL抢走给了f(3),所以f(3)先打印了启动信息,然后进入sleep(5)释放GIL,这时候f(2)才拿回GIL,执行最后的打印。
这种“任务交替执行”的现象完全正常,线程的调度顺序由操作系统决定,加上GIL的释放时机,就会出现看起来“跳步”的输出。
几个关键结论帮你理清逻辑
submit()提交的任务不会等当前任务完成才启动,只要池里有空闲worker就会立刻执行;- 主线程的
cf.wait()只会等待你传入的future对应的任务,不会自动等待后续递归提交的任务; executor.shutdown(wait=True)会等待线程池里所有任务完成,不管这些任务是何时提交的;- 线程之间的执行顺序是不确定的,由操作系统调度+GIL释放时机共同决定,所以不要依赖任务的执行顺序来写代码。
备注:内容来源于stack exchange,提问作者schttrj
相关产品推荐
相关产品推荐

