类中使用Python Multiprocessing无法充分利用CPU的问题求助
解决多进程CPU利用率不足的问题
兄弟,我仔细看了你的代码,发现几个核心问题导致多进程没真正跑起来,CPU利用率上不去,咱们一个个来捋:
1. 最致命的错误:进程创建时直接执行了目标函数
你创建Process的时候写的是:
p = multiprocessing.Process(target=self.run(proc))
这里self.run(proc)是直接在主进程里调用了run方法,而不是把run作为子进程的目标函数!正确的写法应该是把函数对象传给target,参数用args传递:
p = multiprocessing.Process(target=self.run, args=(proc,))
不然子进程根本没干活,所有计算都在主进程串行执行,CPU当然跑不满。
2. 进程串行执行:join()导致一次只能跑一个进程
你的spawn方法里,启动每个进程后立刻调用p.join(),这会阻塞当前线程,必须等这个进程完全结束,才会继续找下一个空闲进程位。相当于你每次只能跑一个进程,完全没用到多进程并行的优势!
3. 子进程直接操作GUI:tkinter不支持跨进程操作
tkinter的所有GUI元素只能在主进程的主线程里操作,子进程直接调用self.update修改Text组件是完全不安全的,会导致程序崩溃或者奇怪的行为。必须用进程间通信(比如队列)让子进程发送消息,主进程自己来更新GUI。
4. 进程状态管理失效:子进程修改的字典不会同步到主进程
self.processes是主进程的字典,子进程启动时会复制一份这个字典,所以子进程里修改self.processes['1'] = 0根本不会影响主进程的状态,主进程永远不知道这个进程已经结束了。
修改后的完整代码示例
我把这些问题都修复了,你可以参考:
import tkinter as tk from tkinter import ttk, filedialog import multiprocessing import time from queue import Empty class main(): def __init__(self,master): self.processes = {'1': None, '2': None, '3': None, '4': None, '5': None, '6': None} self.master = master self.master.title("Framework") self.master.minsize(1450, 650) self.master.maxsize(1450,650) style = ttk.Style() style.theme_use('clam') style.configure('Custom.TButton',background='#94FDFA') # 进程间通信队列:子进程发消息,主进程更新GUI self.msg_queue = multiprocessing.Queue() # GUI元素部分保留你的原有布局,简化重复代码 self.Box4 = Entry(self.master) self.text_frames = {} positions = [ (450, 80, 300, 250), (775, 80, 300, 250), (1100, 80, 300, 250), (450, 370, 300, 250), (775, 370, 300, 250), (1100, 370, 300, 250) ] for idx, (x, y, w, h) in enumerate(positions, 1): text = Text(self.master) scroll = Scrollbar(self.master, command=text.yview) text.configure(yscrollcommand=scroll.set) text.tag_configure('bold_italics', font=('Verdana', 12, 'bold', 'italic')) text.place(x=x, y=y, bordermode="inside", width=w, height=h) scroll.place(x=x+w, y=y, height=h) self.text_frames[str(idx)] = text self.Box4.place(x=10, y=10, bordermode="inside", width=820, height=35) self.b1 = ttk.Button(self.master,text = "Folder", command=self.select_folder) self.b1.place(x=850, y=10) self.b2 = ttk.Button(self.master, text="run", command=self.spawn) self.b2.place(x=20, y=600) self.master.geometry('%dx%d+%d+%d' % (1450, 650, 20, 20)) # 启动主进程的消息监听任务 self.check_queue() def check_queue(self): """主进程定时检查队列,更新GUI和进程状态""" try: while not self.msg_queue.empty(): msg = self.msg_queue.get_nowait() proc_id = msg['proc_id'] content = msg['content'] if content == "finished": # 进程结束,更新状态 self.processes[proc_id] = None self.text_frames[proc_id].insert(tk.END, "任务完成!\n") else: # 更新进度消息 self.text_frames[proc_id].insert(tk.END, content + '\n') self.text_frames[proc_id].see(tk.END) except Empty: pass # 每500ms检查一次队列 self.master.after(500, self.check_queue) def select_folder(self): mas = tk.Toplevel() mas.withdraw() dir_path = filedialog.askdirectory() self.Box4.insert(0, str(dir_path)) self.dir_path = dir_path def spawn(self): """启动空闲的进程,并行执行""" # 找到第一个空闲的进程位 for proc_id in self.processes: if self.processes[proc_id] is None: # 正确创建子进程:传递函数和参数 p = multiprocessing.Process(target=self.run, args=(proc_id, self.msg_queue)) p.start() # 保存进程对象,方便后续管理(比如终止) self.processes[proc_id] = p self.text_frames[proc_id].delete('1.0', tk.END) self.text_frames[proc_id].insert(tk.END, "启动任务...\n") break @staticmethod def run(proc_id, msg_queue): """子进程的计算任务,只负责发消息,不碰GUI""" # 这里替换成你实际的计算逻辑 steps = [ ("done 1/10", 2), ("done 2/10", 5), ("done 3/10", 3), ("done 4/10", 8), ("done 5/10", 2) ] for content, sleep_time in steps: time.sleep(sleep_time) # 发送进度消息到队列 msg_queue.put({'proc_id': proc_id, 'content': content}) # 任务完成,发送结束消息 msg_queue.put({'proc_id': proc_id, 'content': "finished"}) if __name__ == '__main__': # Windows下多进程必须在if __name__ == '__main__'里启动 multiprocessing.freeze_support() master = tk.Tk() ma = main(master) master.mainloop()
关键修改点说明
- 进程创建方式:改用
target=self.run, args=(proc_id, self.msg_queue),确保子进程真正执行计算任务。 - 并行执行:
spawn方法去掉了join(),启动进程后直接继续,多个进程可以同时运行。 - 进程间通信:用
multiprocessing.Queue传递消息,子进程只负责发消息,主进程通过check_queue定时读取并更新GUI,完全符合tkinter的线程/进程安全要求。 - 进程状态管理:主进程保存子进程对象,通过队列消息得知进程结束,更新
self.processes状态。 - 简化代码:把重复的Text组件创建逻辑用循环简化,减少冗余。
这样修改后,你应该能看到CPU利用率明显上升,多个进程并行执行计算任务了。
内容的提问来源于stack exchange,提问作者user10304383
相关产品推荐
相关产品推荐

