autoflush和flush如何不同

时间:2018-01-15 15:56:00

标签: python multithreading sqlite sqlalchemy

我的用户正在与gtk2 GUI(Presenter / View / Model)交互,我想让他们在数据库中导入几条记录,保持界面响应,让用户随时取消操作,以及何时longish操作完成后,让用户决定是否提交整个导入。

我通过在单独的线程中运行导入操作来做到这一点,GUI保持响应,在单独的线程中我创建数据库对象,将它们添加到会话(由演示者拥有),导入线程添加将记录记录到gtk.ListStore,链接到gtk.TreeView,就GUI而言这是好的。当线程最终确定时,“确定”'按钮已启用。

由于这是fairly large program I've adopted的一部分,因此我无法在不审核整个批次的情况下进行更改。其中一个是autoflush,在所有会话中都被禁用。

现在我有一个令人惊讶的行为的小例子:

from sqlalchemy import Column, Unicode, Integer

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class Location(Base):
    __tablename__ = 'location'
    code = Column(Unicode(64), index=True, primary_key=True)
    def __init__(self, code=None):
        self.code = code
    def __repr__(self):
        return self.code

from sqlalchemy import create_engine
engine = create_engine('sqlite:///joindemo.db')

Base.metadata.create_all(engine)

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine, autoflush=False)
session = Session()

session.query(Location).delete()
session.commit()
us = session.query(Location).all()
print us

def run_me_in_other_thread_1():
    session.autoflush = True
    u1 = Location(code=u'mario')
    u2 = Location(code=u'silla')
    session.add_all([u1, u2])
    session.autoflush = False

def run_me_in_other_thread_2():
    u1 = Location(code=u'mario')
    u2 = Location(code=u'silla')
    session.add_all([u1, u2])
    session.flush()

import threading
t = threading.Thread(target=run_me_in_other_thread_1)
t.start()
t.join()

session.commit()
us = session.query(Location).all()
print us

如果你用作线程目标run_me_in_other_thread_1(在会话中暂时启用autoflush),那么一切都如我所料:

[]
[mario, silla]

(不幸的是,启用autoflush,即使只是一次导入,也是我在ghini / bauble中无法做到的事情,我没有精力去研究它。不用说,如果你想看看,fork the project并删除那个令人讨厌的autoflush=False,并设法解决那10个随后的错误测试,成为我的客人!我很乐意为作者列表添加新的贡献者。)

如果禁用autoflush并在从目标函数返回之前执行session.flush(请参阅run_me_in_other_thread_2),则刷新将导致编程错误:

ProgrammingError: (sqlite3.ProgrammingError) SQLite objects created in a thread can only be used in that same thread.The object was created in thread id 140403696158464 and this is thread id 140403623274240

为什么会出现这种差异?

1 个答案:

答案 0 :(得分:0)

我认为我找到了一个解决方案"我如何让线程1告诉线程2如何处理它的数据库对象",它与数据库对象几乎没有关系。

我在GUI中获取了threading.Lock,当用户点击“确定”时或者'取消'按钮,我将选项存储在模块全局变量中,并释放锁定。

另一个线程中的数据库交互有自己的会话,一旦完成,它也会获取锁,这意味着它等待用户做出决定。

基本上就是这样。

为了完整起见,我正在纠正上述代码。

global_ok = False
l = threading.Lock()

def run_me_in_other_thread():
    session = Session()
    u1 = Location(code=u'mario')
    u2 = Location(code=u'silla')
    session.add_all([u1, u2])
    l.acquire()
    if global_ok:
        session.commit()
    else:
        session.rollback()
    l.release()

t = threading.Thread(target=run_me_in_other_thread)

l.acquire()
t.start()

time.sleep(3)
global_ok = True  # or False.
l.release()
t.join()

us = session.query(Location).all()
print us