将Flask flaskr转为Quart项目时,SQLite线程错误问题求助
Quart中SQLite连接关闭时的线程错误解决方案
问题描述
将Flask官方flaskr教程转换为Quart项目时,注册数据库关闭的teardown方法出现线程错误:sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread。需要让teardown方法(路由返回后执行)在创建数据库连接的同一线程中调用close()。
代码示例
#!/usr/bin/env python # -*- coding: utf-8 -*- from sqlite3 import connect from sqlite3 import PARSE_DECLTYPES from sqlite3 import Row from click import command from click import echo from quart import current_app from quart import g from quart.cli import with_appcontext def get_db(): """ Connect to the application's configured database. The connection is unique for each request and will be reused if this is called again. """ if not hasattr(g, "db"): g.db = connect( current_app.config["DATABASE"], detect_types=PARSE_DECLTYPES, ) g.db.row_factory = Row return g.db def close_db(exception=None): db = g.pop("db", None) if db is not None: db.close() @command("init-db") @with_appcontext def init_db_command() -> None: db = get_db() with open(current_app.root_path + "/schema.sql") as file: db.executescript(file.read()) echo("Initialized the database.") def init_app(app) -> None: """ Register database functions with the Quart app. This is called by the application factory. """ app.teardown_appcontext(close_db) app.cli.add_command(init_db_command) return app
错误信息
^^^^^^^^^^^^^^^ File "/root/qcv/venv/lib/python3.11/site-packages/quart/cli.py", line 278, in _inner async with __ctx.ensure_object(ScriptInfo).load_app().app_context(): File "/root/qcv/venv/lib/python3.11/site-packages/quart/ctx.py", line 266, in __aexit__ await self.pop(exc_value) File "/root/qcv/venv/lib/python3.11/site-packages/quart/ctx.py", line 251, in pop await self.app.do_teardown_appcontext(exc) File "/root/qcv/venv/lib/python3.11/site-packages/quart/app.py", line 1169, in do_teardown_appcontext await self.ensure_async(function)(exc) File "/root/qcv/venv/lib/python3.11/site-packages/quart/utils.py", line 57, in _wrapper result = await loop.run_in_executor( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/qcv/qcv/db.py", line 35, in close_db db.close() sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 140328473665600 and this is thread id 140328443631296.
问题原因
Quart默认会将同步的teardown函数放入线程池执行,导致关闭连接的线程与创建连接的线程不一致,触发SQLite的线程安全限制(SQLite默认要求连接只能在创建它的线程中使用)。
解决方案
1. 将close_db改为异步函数
把close_db定义为异步函数,Quart会直接在当前上下文线程中执行它,避免线程切换:
async def close_db(exception=None): db = g.pop("db", None) if db is not None: db.close()
注册方式保持不变:app.teardown_appcontext(close_db),Quart能自动处理异步的teardown方法。
2. 关闭SQLite的线程检查(谨慎使用)
在创建SQLite连接时添加check_same_thread=False参数,关闭线程一致性检查。注意:此方法仅适用于单线程上下文场景(如单worker的Quart服务、CLI命令),多线程共享连接会引发数据安全问题:
def get_db(): if not hasattr(g, "db"): g.db = connect( current_app.config["DATABASE"], detect_types=PARSE_DECLTYPES, check_same_thread=False # 添加该参数 ) g.db.row_factory = Row return g.db
3. 针对CLI命令使用同步上下文装饰器
对于CLI命令,使用@sync_with_appcontext替代@with_appcontext,确保teardown流程在同一个线程中执行:
from quart.cli import sync_with_appcontext @command("init-db") @sync_with_appcontext def init_db_command() -> None: db = get_db() with open(current_app.root_path + "/schema.sql") as file: db.executescript(file.read()) echo("Initialized the database.")
内容的提问来源于stack exchange,提问作者mcpcpc
相关产品推荐
相关产品推荐

