ProgrammingError: 在一个线程中创建的 SQLite 对象只能在同一个线程中使用
Posted
技术标签:
【中文标题】ProgrammingError: 在一个线程中创建的 SQLite 对象只能在同一个线程中使用【英文标题】:ProgrammingError: SQLite objects created in a thread can only be used in that same thread 【发布时间】:2018-06-21 10:22:19 【问题描述】:我对编程很陌生。我以前尝试过 mysql,但现在这是我第一次在 python 烧瓶网站中使用 SQLite。 所以也许我使用的是 MySQL 语法而不是 SQLite,但我似乎找不到问题。
Piece of my code:
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
if request.method=='POST' and form.validate():
name = form.name.data
email = form.email.data
username = form.username.data
password = sha256_crypt.encrypt(str(form.password.data))
c.execute("INSERT INTO users(name,email,username,password)
VALUES(?,?,?,?)", (name, email, username, password))
conn.commit
conn.close()
The error:
File "C:\Users\app.py", line 59, in register c.execute("INSERT INTO users(name,email,username,password) VALUES(?,?,?,?)", (name, email, username, password))
ProgrammingError: SQLite objects created in a thread can only be used in that
same thread.The object was created in thread id 23508 and this is thread id
22640
这是否意味着我不能在 html 文件中使用姓名、电子邮件用户名和密码? 我该如何解决这个问题?
谢谢。
【问题讨论】:
不,它们显然是定义在它上面的 python 对象。错误消息涉及连接和光标。 【参考方案1】:如https://docs.python.org/3/library/sqlite3.html 中所述并由@Snidhi Sofpro 在评论中指出
默认情况下,check_same_thread 为 True,只有创建线程可以使用连接。如果设置为 False,则返回的连接可能会在多个线程之间共享。当使用多个线程时,同一个连接的写入操作应该由用户序列化以避免数据损坏。
实现序列化的一种方式:
import threading
import sqlite3
import queue
import traceback
import time
import random
work_queue = queue.Queue()
def sqlite_worker():
con = sqlite3.connect(':memory:', check_same_thread=False)
cur = con.cursor()
cur.execute('''
CREATE TABLE IF NOT EXISTS test (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT,
source INTEGER,
seq INTEGER
)
''')
while True:
try:
(sql, params), result_queue = work_queue.get()
res = cur.execute(sql, params)
con.commit()
result_queue.put(res)
except Exception as e:
traceback.print_exc()
threading.Thread(target=sqlite_worker, daemon=True).start()
def execute_in_worker(sql, params):
# you might not really need the results if you only use this
# for writing unless you use something like https://www.sqlite.org/lang_returning.html
result_queue = queue.Queue()
work_queue.put(((sql, params), result_queue))
return result_queue.get(timeout=5)
def insert_test_data(seq):
time.sleep(random.randint(0, 100) / 100)
execute_in_worker(
'INSERT INTO test (text, source, seq) VALUES (?, ?, ?)',
['foo', threading.get_ident(), seq]
)
threads = []
for i in range(10):
thread = threading.Thread(target=insert_test_data, args=(i,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
for res in execute_in_worker('SELECT * FROM test', []):
print(res)
# (1, 'foo', 139949462500928, 9)
# (2, 'foo', 139949496071744, 5)
# (3, 'foo', 139949479286336, 7)
# (4, 'foo', 139949487679040, 6)
# (5, 'foo', 139949854099008, 3)
# (6, 'foo', 139949470893632, 8)
# (7, 'foo', 139949862491712, 2)
# (8, 'foo', 139949845706304, 4)
# (9, 'foo', 139949879277120, 0)
# (10, 'foo', 139949870884416, 1)
如您所见,数据被乱序插入,但仍然在while
循环中一一处理。
【讨论】:
【参考方案2】:我遇到了同样的问题,我通过在每次通话后关闭连接来解决它:
results = session.query(something, something).all()
session.close()
【讨论】:
【参考方案3】:你可以试试这个:
engine=create_engine('sqlite:///data.db', echo=True, connect_args="check_same_thread": False)
它对我有用
【讨论】:
【参考方案4】:您的光标“c”不是在同一个线程中创建的;它可能是在运行 Flask 应用程序时初始化的。
您可能希望以相同的方法生成 SQLite 对象(连接和光标),例如:
@app.route('/')
def dostuff():
with sql.connect("database.db") as con:
name = "bob"
cur = con.cursor()
cur.execute("INSERT INTO students (name) VALUES (?)",(name))
con.commit()
msg = "Done"
【讨论】:
我遇到了这个。我正在学习一个似乎有一些 python2 东西的 udacity 全栈开发课程。所以这实际上是一个很好的学习机会。无论哪种方式都对我有用。我在 app.py 文件的顶部有以下内容,然后我从字面上复制并粘贴到该函数(路径)中,瞧,问题解决了。python DBSession = sessionmaker(bind=engine) session = DBSession()
是否还需要con.close()
或with
来解决这个问题?【参考方案5】:
engine = create_engine(
'sqlite:///restaurantmenu.db',
connect_args='check_same_thread': False
)
为我工作
【讨论】:
嗨 J J,欢迎来到 Stack Overflow!请问你能改进这个答案吗?使用单反引号`来显示类似`code`的代码,并解释为什么有问题的代码回答了这个问题/完成了这个人问的工作.. 谢谢,将 args 添加到引擎对我来说效果很好。【参考方案6】:在我的例子中,我有两个创建 sqlite 引擎的 python 文件,因此可能在不同的线程上运行。阅读 SQLAlchemy 文档here,似乎最好在两个文件中使用单例技术:
# maintain the same connection per thread
from sqlalchemy.pool import SingletonThreadPool
engine = create_engine('sqlite:///mydb.db',
poolclass=SingletonThreadPool)
它并不能解决所有情况,这意味着我偶尔会遇到相同的错误,但我可以轻松克服它,刷新浏览器页面。因为我只是用它来调试我的代码,这对我来说没问题。对于更永久的解决方案,可能应该选择另一个数据库,如 PostgreSQL 或其他数据库
【讨论】:
【参考方案7】:在您连接到数据库的位置添加以下内容。
conn = sqlite3.connect('your.db', check_same_thread=False)
【讨论】:
使用安全吗? @uzu,我不明白为什么不这样做,只要您自己进行同步以确保只有一个线程同时使用该对象。 本主题未来读者的一些附加信息。每docs.python.org/3/library/sqlite3.html:默认情况下, check_same_thread 是 True 并且只有创建线程可以使用连接。如果设置为 False,则返回的连接可能会在多个线程之间共享。当使用多个线程时,同一个连接的写操作应该由用户序列化以避免数据损坏。 @merlin2011 你能扩展一下吗?有哪些事情不能做?我不确定我是否完全理解 只要在给定时间内只有一个线程通过连接写入,就可以安全使用。以上是关于ProgrammingError: 在一个线程中创建的 SQLite 对象只能在同一个线程中使用的主要内容,如果未能解决你的问题,请参考以下文章
Django ProgrammingError:在 Django 源代码中创建迁移后,关系已经存在?
psycopg2.ProgrammingError:无法在 odoo 14 中调整类型“NewId”
Python支持SQL数据库返回psycopg2.ProgrammingError:在尝试删除表中的数据时,关系不存在错误?
django.db.utils.ProgrammingError:表不存在