tkinter读取实时更新JSON文件时频繁触发JSONDecodeError
解决跨程序JSON文件读写冲突导致的空JSON错误
问题背景
- 用tkinter实现覆盖层,通过
after()定时读取被其他程序持续更新的JSON文件 - 单独运行时功能正常,手动修改JSON后覆盖层能同步更新;但同时运行两个程序时,会随机触发空JSON解析错误,触发时间无规律(5分钟到45分钟不等)
已尝试但无效的方案
- 先检查JSON格式有效性,再读取文件
- 判断文件大小大于0后再读取
- 分奇偶秒错开两个程序的读写时机
根本原因
两个程序直接对同一文件进行读写时,写操作未完成(比如正在清空文件、写入半完整的JSON)的瞬间,读操作触发,导致读到空内容或不完整的JSON结构,进而引发解析错误。之前的方案要么没解决读写的原子性问题,要么误用time.sleep阻塞tkinter主循环,反而加剧异常概率。
有效解决方案
方案1:跨进程文件锁同步
利用平台原生的文件锁机制,确保同一时间只有一个进程能访问JSON文件,避免读写交叉。
示例代码(兼容Windows/Linux/macOS):
import json import os import sys import tkinter as tk # 跨平台文件锁实现 class FileLock: def __init__(self, file_path): self.file_path = file_path self.lock_file = f"{file_path}.lock" self.fd = None def acquire(self): if sys.platform == 'win32': import msvcrt self.fd = open(self.lock_file, 'w') msvcrt.locking(self.fd.fileno(), msvcrt.LK_NBLCK, 1) else: import fcntl self.fd = open(self.lock_file, 'w') fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) def release(self): if self.fd: if sys.platform == 'win32': import msvcrt msvcrt.locking(self.fd.fileno(), msvcrt.LK_UNLCK, 1) else: import fcntl fcntl.flock(self.fd, fcntl.LOCK_UN) self.fd.close() try: os.remove(self.lock_file) except: pass def read_json_safely(file_path): lock = FileLock(file_path) try: lock.acquire() with open(file_path, 'r', encoding='utf-8') as f: try: return json.load(f) except json.JSONDecodeError: # 读到不完整JSON,返回上次有效数据 return None except (BlockingIOError, PermissionError): # 锁被占用,跳过本次读取 return None finally: lock.release() # tkinter 界面更新逻辑 class OverlayWindow(tk.Tk): def __init__(self): super().__init__() self.label = tk.Label(self, text="等待数据...", font=("Arial", 16)) self.label.pack(padx=20, pady=20) self.last_valid_data = {} self.update_data() def update_data(self): data = read_json_safely("my_json") if data is not None: self.last_valid_data = data self.label.config(text=f"当前值:{data.get('target_key', '无')}") # 用after实现定时,绝不使用time.sleep阻塞主循环 self.after(1000, self.update_data) if __name__ == "__main__": app = OverlayWindow() app.mainloop()
方案2:写程序改用临时文件原子替换(推荐)
修改更新JSON的程序,先将内容写入临时文件,完成后再替换目标JSON文件——文件替换操作是原子性的,读程序永远只会读到完整的JSON内容。
写程序核心代码:
import json import os def write_json_safely(file_path, data): temp_path = f"{file_path}.tmp" # 先写入临时文件 with open(temp_path, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False) # 原子替换目标文件 os.replace(temp_path, file_path)
此方案下读程序无需额外处理,直接正常读取即可,彻底避免不完整JSON的问题。
方案3:严格遵循tkinter异步规则
绝对不要在tkinter主线程中使用time.sleep,所有定时任务必须通过after()实现,否则会阻塞界面渲染和事件处理,引发不可预料的异常。
关键注意事项
- tkinter主循环不能被阻塞,
after()是实现定时任务的唯一正确方式 - 跨进程文件操作必须保证原子性,锁或临时文件二选其一
- 读取时捕获
JSONDecodeError,用上次有效数据兜底,避免界面报错
内容的提问来源于stack exchange,提问作者thalback
相关产品推荐
相关产品推荐

