Python 3.10环境下如何pickle序列化PriorityQueue优先级队列?
可落地的解决方法
不需要找第三方依赖,两种方案都能直接解决问题,改造成本极低:
方案1:保留原有PriorityQueue,自定义序列化规则
queue.PriorityQueue 序列化报错的本质原因是它为了实现多线程同步,内部持有了不可序列化的_thread.lock对象,但你实际存的所有回合优先级、角色对象,都存在它实例的queue属性里——这就是个普通的Python列表,底层靠heapq维护堆结构,本身完全支持pickle。
你只要在持有优先级队列的类里实现__getstate__和__setstate__两个魔术方法,让pickle序列化的时候跳过锁对象、只存核心的堆数据,反序列化的时候重新初始化PriorityQueue再把数据填回去就行,修改后的代码可以直接跑:
import pickle from queue import PriorityQueue class GameEngine(): def __init__(self): self.pqueue = PriorityQueue() def save_to_file(self): with open('save.pkl', 'wb') as file: pickle.dump(self, file, pickle.HIGHEST_PROTOCOL) def __getstate__(self): # 序列化时仅提取底层堆数据,忽略线程锁 return {"pqueue_heap": self.pqueue.queue} def __setstate__(self, state): # 反序列化时重建队列,还原数据 self.pqueue = PriorityQueue() self.pqueue.queue = state["pqueue_heap"] class Monster(): pass engine = GameEngine() orc1 = Monster() orc2 = Monster() engine.pqueue.put((20,orc1)) engine.pqueue.put((10,orc2)) engine.save_to_file() # 验证反序列化后逻辑正常 with open('save.pkl', 'rb') as f: loaded_engine = pickle.load(f) print(loaded_engine.pqueue.get()) # 优先取出优先级值10的orc2 print(loaded_engine.pqueue.get()) # 再取出优先级值20的orc1
这个方案不需要修改你之前写的任何队列操作逻辑,put、get、empty这些PriorityQueue自带的方法都能正常用。
方案2:用标准库heapq实现无锁可序列化优先级队列
既然你完全用不到线程同步功能,其实PriorityQueue本身就是对标准库heapq的线程安全包装,你完全可以直接用heapq操作普通列表实现自己的优先级队列,普通列表天生支持pickle,没有锁开销性能还更好,极简实现参考:
import heapq import pickle class PickleablePriorityQueue: def __init__(self): self._heap = [] def put(self, item): heapq.heappush(self._heap, item) def get(self): return heapq.heappop(self._heap) def empty(self): return len(self._heap) == 0 # 有其他方法需求比如peek、qsize照着补就行,逻辑非常简单 class GameEngine(): def __init__(self): self.pqueue = PickleablePriorityQueue() def save_to_file(self): with open('save.pkl', 'wb') as file: pickle.dump(self, file, pickle.HIGHEST_PROTOCOL)
这个实现的put、get接口和原来的PriorityQueue完全一致,替换成本极低,不需要额外做任何序列化适配。
内容的提问来源于stack exchange,提问作者user2551153
相关产品推荐
相关产品推荐

