Python 在作用域_会话中跨越进程边界

Python 在作用域_会话中跨越进程边界,python,session,sqlalchemy,python-multiprocessing,Python,Session,Sqlalchemy,Python Multiprocessing,我正在使用SQLAlchemy和多处理。我也使用scoped_会话,因为它避免共享同一个会话,但我发现了一个错误及其解决方案,但我不明白为什么会发生这种情况 您可以在下面看到我的代码: db.py engine = create_engine(connection_string) Session = sessionmaker(bind=engine) DBSession = scoped_session(Session) from multiprocessing import Pool, c

我正在使用SQLAlchemy和多处理。我也使用scoped_会话,因为它避免共享同一个会话,但我发现了一个错误及其解决方案,但我不明白为什么会发生这种情况

您可以在下面看到我的代码:

db.py

engine = create_engine(connection_string)

Session = sessionmaker(bind=engine)
DBSession = scoped_session(Session)
from multiprocessing import Pool, current_process
from db import DBSession

def process_feed(test):
    session = DBSession()
    print(current_process().name, session)

def run():
    session = DBSession()
    pool = Pool()
    print(current_process().name, session)
    pool.map_async(process_feed, [1, 2]).get()

if __name__ == "__main__":
    run()
script.py

engine = create_engine(connection_string)

Session = sessionmaker(bind=engine)
DBSession = scoped_session(Session)
from multiprocessing import Pool, current_process
from db import DBSession

def process_feed(test):
    session = DBSession()
    print(current_process().name, session)

def run():
    session = DBSession()
    pool = Pool()
    print(current_process().name, session)
    pool.map_async(process_feed, [1, 2]).get()

if __name__ == "__main__":
    run()
当我运行
script.py
时,输出为:

MainProcess <sqlalchemy.orm.session.Session object at 0xb707b14c>
ForkPoolWorker-1 <sqlalchemy.orm.session.Session object at 0xb707b14c>
ForkPoolWorker-2 <sqlalchemy.orm.session.Session object at 0xb707b14c>
MainProcess <sqlalchemy.orm.session.Session object at 0xb66907cc>
ForkPoolWorker-1 <sqlalchemy.orm.session.Session object at 0xb669046c>
ForkPoolWorker-2 <sqlalchemy.orm.session.Session object at 0xb66905ec>
我再次运行
script.py
,输出为:

MainProcess <sqlalchemy.orm.session.Session object at 0xb707b14c>
ForkPoolWorker-1 <sqlalchemy.orm.session.Session object at 0xb707b14c>
ForkPoolWorker-2 <sqlalchemy.orm.session.Session object at 0xb707b14c>
MainProcess <sqlalchemy.orm.session.Session object at 0xb66907cc>
ForkPoolWorker-1 <sqlalchemy.orm.session.Session object at 0xb669046c>
ForkPoolWorker-2 <sqlalchemy.orm.session.Session object at 0xb66905ec>
main进程
叉子工人-1
工作人员-2

现在会话实例不同了

要理解为什么会发生这种情况,您需要了解
作用域_会话
的实际功能
scoped_session
保留会话注册表,以便执行以下操作

  • 第一次调用
    DBSession
    ,它会在注册表中为您创建一个
    会话
    对象
  • 随后,如果满足必要的条件(即同一线程,会话尚未关闭),它不会创建新的
    会话
    对象,而是返回先前创建的
    会话
    对象
创建
时,它会在
\uuuuu init\uuuuu
方法中创建工人。(请注意,在
\uuuu init\uuuu
中启动辅助进程没有任何基本的意义。同样有效的实现可以等到第一次需要辅助进程后再启动,这在您的示例中会表现出不同的行为。)发生这种情况时(在Unix上),父进程会为每个辅助进程分叉,这涉及到操作系统将当前正在运行的进程的内存复制到一个新进程中,因此您将在完全相同的位置获得完全相同的对象

将这两个问题放在一起,在第一个示例中,您在分叉之前创建了一个
会话
,该会话在创建
期间复制到所有工作进程,从而产生相同的标识,而在第二个示例中,您将
会话
对象的创建延迟到辅助进程启动之后,从而导致不同的身份

需要注意的是,虽然
会话
对象共享相同的
id
,但它们不是同一个对象,因为如果您在父进程中更改有关
会话
的任何内容,它们将不会反映在子进程中。由于fork,它们恰好共享相同的内存地址。但是,操作系统级别的资源(如连接)是共享的,因此,如果您在
池()
之前在
会话
上运行查询,则会在连接池中为您创建一个连接,并随后分岔到子进程中。如果您尝试在子进程中执行查询,您将遇到奇怪的错误,因为您的进程在同一个确切的连接上相互碰撞

上述内容对于Windows没有实际意义,因为Windows没有
fork()

TCP连接表示为文件描述符,文件描述符通常跨进程边界工作,这意味着这将导致代表两个或多个完全独立的Python解释器状态并发访问文件描述符


谢谢!你的解释很清楚。但我认为fork在这里做的是:
pool.map\u async(process\u feed,[1,2]).get()
@Overflow012是的,这是一个同样有效的假设,因为文档没有以这样或那样的方式规定它,但是,作为一个内插细节,它发生在
池中。