Python多线程问题:self.msg_id自增时报‘已定义’错误
问题与解决方案
问题描述
使用Python 3.12.7运行多线程分布式服务器,在handle_message函数中对self.msg_id执行自增操作时触发错误:
[ERROR] Attribute msg_id already defined. Plugin conflict?
读取该变量值完全正常,其余功能均无异常。相关代码如下:
Server类初始化代码
class Server(Bottle): def __init__(self, ID, IP, server_list): super(Server, self).__init__() self.id = int(ID) self.ip = str(IP) self.server_list = server_list self.msg_id = 1 self.status = { "crashed": False, "notes": "", "num_entries": 0, # we use this to generate ids for the entries } self.lock = threading.RLock() # use reentry lock for the server self.msg_lock = threading.RLock() # Handle CORS self.route("/<:re:.*>", method="OPTIONS", callback=self.add_cors_headers) self.add_hook("after_request", self.add_cors_headers) # Those two http calls simulate crashes, i.e., unavailability of the server self.post("/crash", callback=self.crash_request) self.post("/recover", callback=self.recover_request) self.get("/status", callback=self.status_request) # Define REST URIs for the frontend (note that we define multiple update and delete routes right now) self.post("/entries", callback=self.create_entry_request) self.get("/entries", callback=self.list_entries_request) self.put("/entries/<entry_id>", callback=self.update_entry_request) self.delete("/entries/<entry_id>", callback=self.delete_entry_request) # REST URIs for the later algorithms self.post("/message", callback=self.message_request) self.board = Board()
handle_message函数代码
def handle_message(self, message): # Note that you might need to use the lock print("Received message: ", message) if "type" in message: type = message["type"] if type == "add_entry": assert ( self.id == 0 ) # ID 0 is coordinator only this server should receive add_entry messages right now with self.msg_lock: entry_value = message["entry_value"] # We can safely propagate here since we always have a single frontend client, right? So no need to lock, right? Right?! for other in self.server_list: # TODO: Send message to other servers concurrently? self.send_message( other, { "type": "propagate", "entry_value": entry_value, "origin_id": self.id, "message_id": self.msg_id, }, ) self.msg_id += 1 elif type == "propagate": entry_value = message["entry_value"] msg_id = message["message_id"] # Let's hope this is from the coordinator with self.lock: if msg_id in self.board.indexed_entries: return {} self.status["num_entries"] += 1 entry = Entry(msg_id, entry_value) self.board.add_entry(entry) else: print("Received weird message?") return {}
主执行代码
# Sleep a bit to allow logging to be attached time.sleep(2) # the server_list contains all server ips of the distributed blackboard server_list = os.getenv("SERVER_LIST").split(",") own_id = int(os.getenv("SERVER_ID")) own_ip = server_list[own_id] server = Server(own_id, own_ip, server_list) NUM_THREADS = 10 print("#### Starting Server {} with {} threads".format(str(own_id), NUM_THREADS)) httpserver.serve( server, host="0.0.0.0", port=80, threadpool_workers=NUM_THREADS, threadpool_options={"spawn_if_under": NUM_THREADS}, )
问题原因
错误源于Bottle框架的特性:你的Server类继承自Bottle,而Bottle会将实例的顶层属性视为路由模板变量或插件上下文变量。当你尝试修改self.msg_id时,Bottle的内部检测机制会认为该属性已被插件定义,从而抛出"Attribute already defined"的冲突错误。读取操作不会触发这个检测逻辑,因此读取self.msg_id正常。
解决方案
提供三种可行修复方案,可根据代码结构选择:
方案1:将msg_id放入嵌套容器
把msg_id移到已有的self.status字典中,避免作为Server的顶层属性:
# 初始化时修改status字典 self.status = { "crashed": False, "notes": "", "num_entries": 0, "msg_id": 1 # 添加msg_id到这里 } # handle_message中修改时 self.status["msg_id"] += 1 # 发送消息时引用 "message_id": self.status["msg_id"],
方案2:使用私有属性
给msg_id添加双下划线前缀,利用Python的名称修饰机制避开Bottle的检测:
# 初始化时 self.__msg_id = 1 # handle_message中修改 self.__msg_id += 1 # 发送消息时引用 "message_id": self.__msg_id,
方案3:直接操作实例__dict__(不推荐)
如果不想调整属性结构,可以直接修改实例的__dict__绕过Bottle的检测(这种方式破坏封装,仅应急使用):
# 替换原有的self.msg_id += 1 self.__dict__["msg_id"] += 1
内容的提问来源于stack exchange,提问作者Pascal-1609
相关产品推荐
相关产品推荐

