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

关于uWSGI多进程下对象id与线程标识相同的技术疑问

问题描述

使用uWSGI以4进程模式(--processes 4)运行Flask应用时,发现每个进程生成的对象id()值相同,且threading.get_ident()值也一致,日志输出如下:

16:47:09,709|object initiating... obj_id : 139755460182224, pid : 9833, threading.get_ident : 139756071208704|
16:47:09,975|object initiating... obj_id : 139755459113168, pid : 9835, threading.get_ident : 139756071208704|
16:47:09,982|object initiating... obj_id : 139755459113168, pid : 9832, threading.get_ident : 139756071208704|
16:47:10,022|object initiating... obj_id : 139755459113168, pid : 9834, threading.get_ident : 139756071208704|

原本认为进程间内存不共享,基于内存地址的id()值不应相同;虽知晓是相对内存地址,但疑惑为何每次都一致。同时不同进程的线程标识也相同,无法理解uWSGI的多进程机制。

相关代码与启动命令:

  • temp.py定义类A:
import os
import threading
import logging

logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
formatter = logging.Formatter("%(asctime)s|%(levelname)s|object|%(message)s|")
handler.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(handler)


class A(object):

    def __init__(self):
        logger.info(f" object initiating... obj_id : {id(self)} pid : {os.getpid()} thread_id : {threading.get_ident()}")
  • hello.py创建实例:
import logging
from flask import Flask
from temp import A

app = Flask(__name__)

a = A()

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

if __name__ == '__main__':
    app.run()
  • wsgi.py入口:
from hello import app

if __name__ == '__main__':
    app.run()
  • 启动命令:
~/.virtualenvs/testflask/bin/uwsgi --http :5004 --module wsgi:app --logto ./log.log --processes 4 --enable-threads --single-interpreter --master --lazy-apps
原因分析

1. 对象id()值相同的本质

每个进程拥有完全独立的虚拟地址空间,Python的id()返回的是对象在当前进程虚拟地址空间中的内存地址,而非物理内存地址。

你使用了--lazy-apps参数,这个参数让每个uWSGI worker进程独立加载Flask应用,而非由master进程加载后fork子进程。由于每个worker的应用加载流程完全一致:代码执行顺序、内存分配逻辑完全相同,导致类A的实例在每个进程的虚拟地址空间中被分配到了相同的位置,因此id()值完全一致。

但注意:这些相同的虚拟地址对应不同进程的物理内存,对象本身是完全独立的,修改其中一个进程的对象不会影响其他进程。

2. threading.get_ident()值相同的原因

threading.get_ident()返回的是当前进程内的线程标识,这个标识仅在当前进程的线程环境中有意义,不跨进程。

每个uWSGI worker进程启动后,执行应用初始化(创建A实例)的是该进程的主线程。由于每个worker进程的主线程都是进程启动后的第一个线程,其标识在各自进程内的线程ID序列中都是初始值,因此不同进程的主线程标识会出现相同的情况。

3. uWSGI多进程机制的核心

  • --master:启动主进程,负责管理worker进程的生命周期(启动、监控、重启),主进程不处理请求。
  • --processes 4:启动4个worker进程,每个worker都是独立的操作系统进程,拥有自己的内存空间、文件描述符,进程间完全隔离,内存不共享。
  • --lazy-apps:禁止master进程预加载应用,改为每个worker进程独立加载应用,避免fork带来的内存复制问题,同时保证每个worker的应用状态独立。
验证建议

可以通过以下方式验证进程间的内存隔离:
修改hello.py,在路由中打印当前进程的pid和全局对象a的属性:

@app.route("/")
def hello_world():
    import os
    import random
    # 给a添加随机属性
    setattr(a, 'rand_val', random.randint(1, 100))
    return f"<p>Hello, World! PID: {os.getpid()}, A.rand_val: {a.rand_val}, A.id: {id(a)}</p>"

多次访问路由,会发现不同PID的进程返回的rand_val完全独立,即使id(a)相同,也证明对象是各自进程内的独立实例。

内容的提问来源于stack exchange,提问作者쿨해머

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 18:24:54