Python:api调用上的sqlite线程错误

Python:api调用上的sqlite线程错误,python,multithreading,sqlite,api,Python,Multithreading,Sqlite,Api,因此,我有一个应用程序结构如下(高度简化): 相关的main.py内容如下所示: db= database.DataBaseWrapper(db_name) handler = operations_handler.OperationHandler(args, db) api_server = api.APIServer() api_server.run(handler) 所有处理程序方法都可以正常工作。api方法只是对处理程序方法的包装调用。 现在的问题是api,它在收到请求时启动新的工作

因此,我有一个应用程序结构如下(高度简化):

相关的main.py内容如下所示:

db= database.DataBaseWrapper(db_name)
handler = operations_handler.OperationHandler(args, db) 
api_server = api.APIServer()
api_server.run(handler)
所有处理程序方法都可以正常工作。api方法只是对处理程序方法的包装调用。 现在的问题是api,它在收到请求时启动新的工作线程:

WARNING:root:at api method called <WorkerThread \ 
(CP Server Thread-1, started 139656470746880)> 

WARNING:root:at api method called <WorkerThread \
   (CP Server Thread-2, started 139656462354176)>
任何游标或数据库对象都不会从数据库模块中传出,所有游标都将关闭。但是,即使处理程序对象在两个线程中都是相同的,sqlite在访问任何api方法时都会引发错误?IDK这个想法是什么,我必须为每个线程建立一个新的数据库连接吗?为什么一个数据库对象被不同的线程引用是不够的?关于这方面的文档非常稀少…

这在以下章节中进行了解释:

较旧的SQLite版本在线程之间共享连接方面存在问题。这就是Python模块不允许在线程之间共享连接和游标的原因。如果您仍然尝试这样做,您将在运行时得到一个异常


这实际上在构建时是可配置的,但大多数人不会从头开始配置和构建Python和stdlib。但是,请注意,文档链接到的页面,指出“
sqlite3
是在外部以名称
pysqlite
开发的”,如果确实需要免费线程,您可以构建和安装
pysqlite
,并使用它代替stdlib模块


事实上,IIRC,在
pysqlite
2.5/python3.4,
sqlite3
附近, 有一个未完全记录的特性,允许您通过将
check\u same\u thread=False
传递到
连接
构造函数来禁用线程安全检查(然后您创建的任何
游标
对象将从
连接继承它)。如果这样做,那么就可以在线程之间安全地共享对象,但仍然不能并行使用它们,只能在互斥对象中使用,因为并发性还没有经过全面测试(这也是为什么它不是一个完全没有文档记录的特性,而不是一个有文档记录的特性)


当然,正如文档所说,可移植的方法是不在线程之间共享连接和游标

一种方法是让每个
WorkerThread
(甚至每个任务,如果它们是线程池的一部分)创建自己的
连接。打开一组连接的性能成本并没有那么高。但是,请确保您阅读了有关并发访问和线程安全的说明。实际上,如果您希望完全可移植(包括网络共享驱动器上的数据库文件、SQLite的旧版本等),您将需要创建一个共享的
线程锁
,并让您的线程在每次访问数据库(包括其初始
连接
构造函数)时获取该锁

另一种方法是在单个线程上运行所有SQLite查询,让其他线程只提交一个查询,然后返回一个他们可以阻止的未来。如果您需要Python3.2+,或者PyPI backport(它可以回溯到2.6,IIRC),那么这是最简单的方法。如果没有,您可以很容易地构建未来和单线程执行器,例如,每个未来的线程、队列和条件

我个人曾多次使用futures解决方案。看起来你在抛弃很多平行性,但实际上你没有;如果您使用来自多个线程的连接,它将使用自己的互斥量,并且还要求您在其上添加互斥量;如果使用多个连接,它会使用文件锁,而文件锁更重。如果单线程执行器没有为您提供足够的并发性,那么摆脱它也可能不会,您需要使用MySQL或类似的工具来代替SQLite。

这在以下章节中进行了解释:

较旧的SQLite版本在线程之间共享连接方面存在问题。这就是Python模块不允许在线程之间共享连接和游标的原因。如果您仍然尝试这样做,您将在运行时得到一个异常


这实际上在构建时是可配置的,但大多数人不会从头开始配置和构建Python和stdlib。但是,请注意,文档链接到的页面,指出“
sqlite3
是在外部以名称
pysqlite
开发的”,如果确实需要免费线程,您可以构建和安装
pysqlite
,并使用它代替stdlib模块


事实上,IIRC,在
pysqlite
2.5/python3.4,
sqlite3
附近, 有一个未完全记录的特性,允许您通过将
check\u same\u thread=False
传递到
连接
构造函数来禁用线程安全检查(然后您创建的任何
游标
对象将从
连接继承它)。如果这样做,那么就可以在线程之间安全地共享对象,但仍然不能并行使用它们,只能在互斥对象中使用,因为并发性还没有经过全面测试(这也是为什么它不是一个完全没有文档记录的特性,而不是一个有文档记录的特性)


当然,正如文档所说,可移植的方法是不在线程之间共享连接和游标

一种方法是让每个
WorkerThread
(甚至每个任务,如果它们是线程池的一部分)创建自己的
连接。打开一组连接的性能成本并没有那么高。但是,请确保您阅读了有关并发访问和线程安全的说明。实际上,如果您希望完全可移植(包括网络共享驱动器上的数据库文件、SQLite的旧版本等),您将需要创建一个共享
线程。锁定
,并让线程在每次访问数据库(包括其初始

WARNING:root:at api method called <WorkerThread \ 
(CP Server Thread-1, started 139656470746880)> 

WARNING:root:at api method called <WorkerThread \
   (CP Server Thread-2, started 139656462354176)>
'message': 'SQLite objects created in a thread can only be used in that \
same thread.The object was created in thread id 139656604452608 \ 
and this is thread id 139656462354176'