You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 13:01:10