【问题标题】:Using sqlalchemy scoped_session in theading.Thread在 theading.Thread 中使用 sqlalchemy scoped_session
【发布时间】:2015-10-11 02:46:48
【问题描述】:

我在使用 sqlalchemy 和线程时遇到问题。

import queue
import threading

import sqlalchemy
from sqlalchemy import create_engine, Column, Integer, String, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.scoping import scoped_session

engine = create_engine('sqlite:///:memory:', echo=False)
Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    name = Column(String)
    fullname = Column(String)
    password = Column(String)

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (
        self.name, self.fullname, self.password)
Base.metadata.create_all(engine)

sessionfactory = sessionmaker(bind=engine)

# called by each thread
def write_name(q, name, sessionfactory):
    session = scoped_session(sessionfactory)
    ed_user = User(name=name, fullname='Power', password='edspassword')
    session.add(ed_user)
    session.commit()
    q.put(name)

names = ["Max", "Austin"]

q = queue.Queue()

for u in names:
    t = threading.Thread(target=write_name, args = (q, u, sessionfactory))
    t.daemon = True
    t.start()

s = q.get()

这会导致:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users [SQL: 'INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)'] [parameters: ('Max', 'Power', 'edspassword')]

但在主线程中添加和读取数据效果很好。此外,我假设我需要在多进程上使用线程,因为 scoped_session 使用线程本地存储。

【问题讨论】:

    标签: python multithreading sqlalchemy python-3.4


    【解决方案1】:

    主要问题是你不能有多个连接到只存在于内存中的 SQLite 数据库,因为每个连接都会创建一个新的空数据库。请参阅SQLAlchemy docs。简而言之,您需要像这样创建引擎,以确保它只是一个可以跨线程共享的实例。

    from sqlalchemy.pool import StaticPool
    engine = create_engine('sqlite://:memory:',
        connect_args={'check_same_thread': False},
        poolclass=StaticPool, echo=True)
    

    一旦你这样做了,你就不需要scoped_session,因为scoped_session 的目的是为每个线程创建一个连接,而你在这里特别不能这样做。

    另外,请注意,如果您希望它正常工作(使用非 SQLite 引擎),您应该只有一个 scoped_session 实例。您应该将其视为全局变量,然后它将能够处理线程局部的东西。

    【讨论】:

    • treat it as global variable and then it will be able to handle the thread-local stuff。这是关于scoped_session 的部分,这让我非常震惊。它是如何工作的?
    • @rr- 它使用threading.local() 使对象仅对创建它们的线程可用。因此,每次您使用作用域会话时,它都会查看这个线程本地存储,如果有会话,它将使用它。如果没有,它将创建一个新的并将其存储在那里以备将来使用。可以看到代码herehere
    猜你喜欢
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-09
    • 1970-01-01
    • 2011-09-25
    • 2011-07-29
    • 2013-08-27
    相关资源
    最近更新 更多