多进程文件加密场景下tkinter ProgressBar进度异常问题求助
Tkinter多进程文件加密程序进度条异常修复
核心问题原因
- Tkinter控件不是线程安全的,原代码在线程池的工作线程中直接修改进度条属性、调用
root.update_idletasks(),跨线程操作UI会导致更新时序混乱,出现进度跳变、卡顿问题 enc函数内的计数变量x为局部变量,每次处理文件都会被重置为0,百分比计算逻辑完全错误- 进程池使用阻塞的
apply()方法调用加密函数,无法充分发挥多进程并发能力 - 没有全局线程安全的进度计数器,无法正确累计已完成的加密任务数
修复实现
首先新增全局计数器和锁保证计数安全:
import threading completed_tasks = 0 task_lock = threading.Lock()
修改加密逻辑,移除工作线程中的UI操作,通过事件机制通知主线程更新UI:
def enc(key, process_pool, file): with open(file,'rb') as original_file: original = original_file.read() encrypted = process_pool.apply(encryptfn, args=(key, original,)) with open (file,'wb') as encrypted_file: encrypted_file.write(encrypted) # 线程安全累加计数 global completed_tasks with task_lock: completed_tasks +=1 # 仅向主线程发送更新通知,不直接操作UI root.event_generate("<<update_progress>>", when="tail")
在主线程绑定进度更新事件,所有UI操作统一在主线程执行:
def update_progress(*args): global completed_tasks total = len(files) progress = (completed_tasks / total) * 100 pbar['value'] = progress percent.set(f"{int(progress)}%") root.update_idletasks() # 主程序初始化时绑定事件 progbar() percent.set('0%') root.bind("<<update_progress>>", update_progress)
可进一步简化池逻辑,移除冗余的线程池嵌套,直接用进程池异步实现:
def encfile(): global completed_tasks, files completed_tasks = 0 password = bytes('asdasd', 'utf-8') salt = bytes('zxcasd','utf-8') files = filistbox.get(0,'end') if len(files) == 0: fierrorbox() return elif len(password) == 0: passerrorbox() return file_enc_button['state']='disabled' browsefi['state']='disabled' kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100, backend=default_backend()) key = base64.urlsafe_b64encode(kdf.derive(password)) pool_size = min(cpu_count(), len(files)) with Pool(pool_size) as process_pool: results = [process_pool.apply_async(encryptfn, args=(key, open(f,'rb').read())) for f in files] for idx, res in enumerate(results): encrypted = res.get() with open(files[idx], 'wb') as f: f.write(encrypted) completed_tasks +=1 root.event_generate("<<update_progress>>", when="tail") root.event_generate("<<encryption_done>>") file_enc_button['state']='active' browsefi['state']='active'
内容的提问来源于stack exchange,提问作者Sanket
相关产品推荐
相关产品推荐

