我正在构建一个应用程序,根据用户输入的内容进行数千次(可能是数百万次)的计算。因为这些计算可能需要很长时间,所以我想使用Python的多处理模块。这是我的困境;我想设置一个带有取消按钮的tkinter窗口,以停止在我使用Pool设置的处理器中运行的计算。我尝试使用线程来允许弹出窗口运行,但是当我运行代码时会发生一些有趣的事情。
当我按下'开始'按钮,池按预期开始运行。但是,即使它在自己的线程上,我的弹出窗口也不显示。甚至更奇怪,当我告诉我的IDE(PyCharm)停止程序时,弹出窗口显示并且计算仍在运行,直到我按下退出'弹出窗口中的按钮,或者完全杀死程序。
所以我的问题是:为什么我的弹出窗口显示,即使它在自己的线程上?我正确使用多处理模块吗?或问题是完全不同的?
import tkinter as tk
from tkinter import ttk
import random
import threading
import time
from multiprocessing import Pool
def f(x):
print(x*x)
time.sleep(random.randrange(1,5)) # simulates long calculations
# Extra math stuff here...
def processor():
global calc
calc = Pool(4)
calc.map(f, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30])
print("Done")
def calculation_start():
p = threading.Thread(target=progress_window) # Thread to open a new window for the user to cancel a calculation
p.start()
processor() # Calls the function to run the calculations
def progress_window():
# This is the window that pops up to let a user cancel a calculation.
popup = tk.Toplevel()
endButton = ttk.Button(popup, text="End", command=lambda :(calc.terminate()))
endButton.pack()
master = tk.Tk()
startButton = ttk.Button(master, text="Start", command=calculation_start)
startButton.pack()
tk.mainloop()
编辑:
我尝试将processor
函数切换到线程而不是progress_window
函数。
def calculation_start():
p = threading.Thread(target=processor) # Thread for the function to run the calculations
p.start()
progress_window() # Open a new window for the user to cancel a calculation
出现我的弹出窗口,结束'按钮看起来像按下时停止处理器,但它永远不会继续超过该点。它就像calc.map(f, [1,2,3,...]
函数中的processor()
一样停滞不前。
答案 0 :(得分:0)
来自Pool.map
文档:
它会阻塞,直到结果准备就绪。
这意味着,是的,工作正在其他线程上完成,但主线程(调用map
的线程)在其他线程完成之前不会执行任何其他操作。您希望使用map_async
和(对于长时间运行的交互式进程),提供一个回调,以便在池完成时执行操作(可能隐藏弹出窗口)。
如果您希望程序只显示弹出窗口然后退出,则需要使用map_async
返回的AsyncResult
。使用after_idle
设置空闲处理程序(请参阅tkinter universal widget methods并记住您需要在每次调用后手动重新添加处理程序)。在每次空闲呼叫之后,您可以呼叫AsyncResult.ready()
或AsyncResult.get(<timeout>)
以查看该池是否已完成。一旦完成,您就可以安全地处理结果并退出。