操作系统:windows 10
python 3.7.6
- 我有一个任务函数
def mytest(task_queue):
while True:
print(task_queue.get())
- 我想运行子线程并等待其他人将一些东西放入
task_queue
- 如果我使用
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
- 如果我通过
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
- 但我不认为这两个方法中的任何一个会阻塞代码,因为它们只是启动线程
- 所以我有两个问题:
- 为什么
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
中。但是,将任务添加到队列中会迫使调用方等待,直到队列中有足够的空间。
一段时间后,当任务最终到达队列的头部时,工作线程将接受任务并执行它