首先,我不知道“所有权”是否是正确的术语,这正是我在Java中所称的。
我目前正在构建一个使用SQLite的服务器,我遇到有关对象“所有权”的错误:
我有一个管理SQLite数据库的模块。我们称之为“pyDB”。简化为:
import threading
import sqlite3
class DB(object):
def __init__(self):
self.lockDB = threading.Lock()
self.conn = sqlite3.connect('./data.sqlite')
self.c = self.conn.cursor()
[...]
def doSomething(self,Param):
with self.lockDB:
self.c.execute("SELECT * FROM xyz WHERE ID = ?", Param)
(注意lockDB
对象是存在的,因为Database-Class可以由多个并发线程调用,虽然SQLite本身是线程安全的,但是cursor
- Object不是,到目前为止据我所知)。
然后我有一个处理东西的工作线程。
import pyDB
self.DB = pyDB.DB()
class Thread(threading.Thread):
[omitting some stuff that is not relevant here]
def doSomethingElse(self, Param):
DB.doSomething(Param)
如果我执行此操作,我将收到以下异常:
self.process(task)
File "[removed]/ProcessingThread.py", line 67, in process
DB.doSomething(Param)
File "[removed]/pyDB.py", line 101, in doSomething
self.c.execute(self,"SELECT * FROM xyz WHERE ID = ?", Param)
ProgrammingError: SQLite objects created in a thread can only be used in that same
thread.The object was created in thread id 1073867776 and this is thread id 1106953360
现在,据我所知,这是我遇到的问题earlier(其中对象所有权不是给初始化的类,而是给那个调用它的那个。或者我理解它) ,这让我终于接受了我一般不理解Python中的对象所有权是如何工作的。我已经在Python文档中找到了可以理解的解释,但是没有找到任何解释。
所以,我的问题是:
我很乐意为这个案例提出具体的建议,但我一般对Python中“什么属于谁”的整个概念更感兴趣,因为对我而言,它似乎与Java处理它的方式完全不同,我计划将来使用Python很多,我现在也可以学习它,因为这是Python非常重要的一部分。
答案 0 :(得分:4)
ProgrammingError:在线程中创建的SQLite对象只能在同一个
中使用
问题是你出于某种原因试图保存光标。你不应该这样做。为每个事务创建一个新游标;或者,如果您不完全确定事务的开始或结束位置,则每个查询都有一个新游标。
import sqlite3
class DB(object):
def __init__(self):
self.conn_uri = './data.sqlite'
[...]
def doSomething(self,Param):
conn = sqlite.connect(self.conn_uri)
c = conn.cursor()
c.execute("SELECT * FROM xyz WHERE ID = ?", Param)
编辑,重新评论您的问题:这里发生的事情与python几乎没什么关系。当您创建一个sqlite资源(一个C库并且完全独立于python)时,sqlite要求该资源仅在创建它的线程中使用 。它通过查看当前正在运行的线程的线程ID来验证这一点,而根本不会尝试协调资源从一个线程到另一个线程的传输。因此,您有义务在需要它们的每个线程中创建sqlite资源。
在您的代码中,您可以在DB
对象的__init__
方法中创建所有sqlite资源,该方法可能只调用一次,并在主线程中。因此,这些资源只允许在该线程中使用,threading.Lock
不能承受。
您的问题:
- 在这种情况下谁拥有游标对象?处理线程或数据库线程?
创建它的线程。因为看起来你在模块级别调用DB()
,所以它很可能是主线程。
- 我在哪里可以阅读这些东西,最终“得到”它?
没有什么可以得到的。除了SQLite has to say on the matter,当你使用它时,幕后一切都没有发生。
除了允许你使用线程之外,Python并没有太多与线程有关的东西。您可以正确协调多线程应用程序。
- 术语“对象所有权”是否正确,或者在Python中是否还有其他术语?
再次编辑:
对象不在特定线程内。在对象上调用方法时,该方法在调用线程中运行。十个线程可以在同一个对象上调用相同的方法; all将同时运行(或者GIL的任何传递),并且由调用者或方法体来确保没有任何中断。
答案 1 :(得分:2)
我是Python (APSW)的备用SQLite包装器的作者,并且非常熟悉这个问题。 SQLite本身曾经要求对象 - 数据库连接和游标只能在同一个线程中使用。在SQLite 3.5周围,这已被更改,你可以同时使用对象,虽然内部SQLite自己锁定,所以你实际上并没有获得并发性能。默认的Python SQLite包装器(也称为pysqlite)甚至支持旧版本的SQLite 3,因此它继续强制执行此限制,即使SQLite本身不再需要它。然而,需要修改pysqlite代码以允许并发,因为它包装SQLite的方式并不安全 - 例如,由于SQLite API设计缺陷而需要特殊处理,因此处理错误消息并不安全。
请注意,游标非常便宜。不要试图重复使用它们或将它们视为珍贵的。实际的底层SQLite对象(sqlite3_stmt)保存在缓存中并根据需要重用。
如果您确实需要最大并发性,请打开多个连接并同时使用它们。
APSW doc更多关于multi-threading and re-entrancy。请注意,它有额外的代码来允许pysqlite没有的实际并发使用,但其他提示和信息适用于SQLite的任何使用。