我的用户正在与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
为什么会出现这种差异?
答案 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