Python - 为什么 ThreadPoolExecutor().submit 一个带有队列块的线程,而 Thread().start 没有?



操作系统:windows 10

python 3.7.6

  1. 我有一个任务函数
def mytest(task_queue):
while True:
print(task_queue.get())
  1. 我想运行子线程并等待其他人将一些东西放入task_queue
  2. 如果我使用concurrent.futures.ThreadPoolExecutor().submit()启动线程,然后把一些东西放入队列,它就会阻塞,task_queue.put(1)永远不会运行
if __name__ == '__main__':
import queue
task_queue = queue.Queue()
task_queue.put(0)
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.submit(mytest, task_queue)
task_queue.put(1)
task_queue.put(2)
# only print 0, then block 
  1. 如果我通过Thread().start()启动线程,它将按预期工作
if __name__ == '__main__':
import queue
task_queue = queue.Queue()
task_queue.put(0)
t1 = threading.Thread(target=mytest, args=(task_queue,))
t1.start()
task_queue.put(1)
task_queue.put(2)
# print 0, 1, 2. but the main thread does not exit
  1. 但我不认为这两个方法中的任何一个会阻塞代码,因为它们只是启动线程
  2. 所以我有两个问题:
    • 为什么submit()会阻塞代码
    • 使用start()启动没有join()的子线程时,为什么主线程不退出

THX

Q-1(为什么submit()会阻塞代码?

A-1(没有submit()方法不阻塞,它将可调用的mytest调度为mytest(task_queue)执行,并返回Future对象。看看下面的代码,你会发现submit()方法不会阻塞主线程

if __name__ == '__main__':
import queue
task_queue = queue.Queue()
task_queue.put(0)
executor = ThreadPoolExecutor()
executor.submit(mytest, task_queue)
task_queue.put(1)
task_queue.put(2)

print("hello")
>> 0
hello
1
2

或者你可以这样做:

if __name__ == '__main__':
import queue
task_queue = queue.Queue()
task_queue.put(0)
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.submit(mytest, task_queue)
task_queue.put(1)
task_queue.put(2)

你会看到task_queue.put(1)和其他将立即被调用

正如您在上面的例子中看到的,submit()方法不阻塞,但当您将with语句与concurrent.futures.ThreadPoolExecutor()一起使用时,__exit__()方法将被称为with语句的末尾。此__exit__()方法将调用ThreadPoolExecutor()类的shutdown(wait=True)方法。当我们看关于shutdown(wait=True)方法的文档时:

如果wait为True,则此方法将不会返回,直到所有挂起的期货执行完毕,与遗嘱执行人已被释放。如果wait为False,则此方法将立即返回,与执行器关联的资源将在所有挂起的期货执行完毕后释放。不管等待的值,整个Python程序将不会退出,直到所有挂起的期货执行完毕。

这就是主线程阻塞with语句末尾的原因。

我想回答你的第二个问题,但我对主线程退出与否感到困惑。我稍后会编辑这个答案(针对第二个问题(

Thread(...).start()创建一个新线程。故事结束了。如果还有一些内存可以创建,那么你总是可以创建一个新线程

executor.submit(mytest, task_queue)创建一个新的任务并将其添加到task_queue中。但是,将任务添加到队列中会迫使调用方等待,直到队列中有足够的空间。

一段时间后,当任务最终到达队列的头部时,工作线程将接受任务并执行它

最新更新