gpt4 book ai didi

mod-wsgi - 全局Python对象的唯一性在子解释器中无效?

转载 作者:行者123 更新时间:2023-11-30 21:50:36 33 4
gpt4 key购买 nike

我对 Python 子解释器初始化(来自 Python/C API)和 Python id() 函数的内部工作有疑问。更准确地说,是关于在 WSGI Python 容器中处理全局模块对象(例如与 nginx 一起使用的 uWSGI 和 Apache 上的 mod_wsgi)。

以下代码在上述两种环境中都按预期工作(隔离),但我无法向自己解释为什么id()函数总是返回相同的值 每个变量,无论执行它的进程/子解释器如何。

from __future__ import print_function
import os, sys

def log(*msg):
print(">>>", *msg, file=sys.stderr)

class A:
def __init__(self, x):
self.x = x
def __str__(self):
return self.x
def set(self, x):
self.x = x

a = A("one")
log("class instantiated.")

def application(environ, start_response):

output = "pid = %d\n" % os.getpid()
output += "id(A) = %d\n" % id(A)
output += "id(a) = %d\n" % id(a)
output += "str(a) = %s\n\n" % a

a.set("two")

status = "200 OK"
response_headers = [
('Content-type', 'text/plain'), ('Content-Length', str(len(output)))
]
start_response(status, response_headers)

return [output]

我已经在 uWSGI 中用一个主进程和 2 个工作进程测试了这段代码;在 mod_wsgi 中,使用具有两个进程和每个进程一个线程的守护进程模式。典型的输出是:

pid = 15278
id(A) = 139748093678128
id(a) = 139748093962360
str(a) = one

第一次加载时,然后:

pid = 15282
id(A) = 139748093678128
id(a) = 139748093962360
str(a) = one

第二个,然后

pid = 15278 | pid = 15282
id(A) = 139748093678128
id(a) = 139748093962360
str(a) = two

彼此之间。正如您所看到的,类和类实例的 id()(内存位置)在两个进程(上面的第一个/第二个加载)中保持相同,而在同时类实例存在于单独的上下文中(否则第二个请求将显示“两个”而不是“一个”)!

我怀疑 Python 文档可能暗示了答案:

id(object):

Return the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

但如果这确实是原因,那么我对下一个声明 id() 值是对象地址的语句感到困扰!

虽然我很欣赏这个事实,但这很可能只是一个 Python/C API“聪明”功能,可以解决(或者更确切地说修复) problem of caching object references (pointers) in 3rd party extension modules ,我仍然觉得这种行为不符合……嗯,常识。有人可以解释一下吗?

我还注意到 mod_wsgi 在每个进程中导入模块(即两次),而 uWSGI 对于两个进程仅导入模块一次。由于 uWSGI 主进程执行导入,我想它会为子进程播种该上下文的副本。两个工作人员随后独立工作(深度复制?),同时看似使用相同的对象地址。 (此外,工作人员在重新加载时会重新初始化为原始上下文。)

对于这么长的帖子,我深表歉意,但我想提供足够的细节。谢谢!

最佳答案

您要问的问题并不完全清楚;如果问题更具体,我会给出更简洁的答案。

首先,对象的 id 实际上是(至少在 CPython 中)它在内存中的地址。这是完全正常的:同一进程中的两个对象不能共享地址,并且对象的地址在 CPython 中永远不会改变,因此地址可以很好地用作 id。我不知道这怎么违背常识。

接下来,请注意后端进程可能以两种截然不同的方式产生:

  • 通用 WSGI 后端处理程序将 fork 进程,然后每个进程将启动一个后端。这很简单且与语言无关,但会浪费大量内存并浪费时间重复加载后端代码。
  • 更高级的后端将加载 Python 代码一次,然后在加载后 fork 服务器的副本。这使得代码只需加载一次,速度更快,并且显着减少内存浪费。这就是生产质量的 WSGI 服务器的工作原理。

但是,这两种情况的最终结果是相同的:独立的 fork 进程。

那么,为什么你们最终会得到相同的 ID?这取决于使用上述哪种方法。

  • 使用通用 WSGI 处理程序时,发生这种情况只是因为每个进程本质上都在做相同的事情。只要进程在做同样的事情,它们最终就会得到相同的 ID;在某些时候他们会出现分歧,这种情况将不再发生。
  • 使用预加载后端时,会发生这种情况,因为此初始代码仅在服务器 fork 之前发生一次,因此可以保证具有相同的 ID。

但是,无论如何,一旦 fork 发生,它们就是单独的对象,在不同的上下文中。不同进程中具有相同 ID 的对象没有任何意义。

关于mod-wsgi - 全局Python对象的唯一性在子解释器中无效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4146630/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com