了解多模块环境中的Python-sqlite机制

2024-10-01 07:16:15 发布

您现在位置:Python中文网/ 问答频道 /正文

首先,我不知道“所有权”是否是正确的术语,这正是我在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对象的存在是因为数据库类可以被多个并发线程调用,尽管SQLite本身是线程安全的,但据我所知,cursor-对象不是线程安全的)。在

然后我有一个处理东西的工作线程。在

^{pr2}$

如果执行此操作,则会出现以下异常:

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文档中找到了一个可以理解的解释,但是没有找到任何解释。在

所以,我的问题是:

  • 在这种情况下,谁拥有游标对象?处理线程还是数据库线程?在
  • 我在哪里可以读到这些东西,最终“得到”它?在
  • 术语“Objectownership”是否正确,或者在Python中还有其他术语可以用来描述这个问题?(编辑:关于这方面的解释,请阅读主要问题的评论)

对于这种情况,我很乐意接受具体的建议,但一般来说,我更感兴趣的是Python中“属于谁的东西”的整个概念,因为对我来说,它似乎与Java处理它的方式非常不同,而且由于我计划在将来大量使用Python,所以我最好现在就学习它,因为这是Python非常重要的一部分。在


Tags: 对象inimportself数据库sqliteparamjava
2条回答

ProgrammingError: SQLite objects created in a thread can only be used in that same

问题是由于某种原因,您试图保存光标。你不应该这样做。为每个事务创建一个新的游标;或者,如果您不完全确定事务的开始或结束位置,则为每个查询创建一个新的游标。在

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尽管如此。在

您的问题:

  • Who owns the cursor object in this case? The Processing Thread or the DB thread?

创建它的线程。因为它看起来像是在模块级别调用DB(),所以它很可能是主线程。在

  • Where can I read up on this stuff to finally "get" it?

其实没什么好弄的。在幕后什么都没有发生,除了当您使用它时SQLite has to say on the matter什么。在

  • Is the term "Object ownership" even correct, or is there an other term for this in Python?

Python实际上与线程没有太多关系,只是它允许您使用线程。正确地协调多线程应用程序是你的责任。在

再次编辑:

对象不在特定线程内。当您调用对象上的方法时,该方法在调用线程中运行。十个线程可以在同一个对象上调用同一个方法;所有线程都将并发运行(或者传递给GIL的任何线程),并且由调用方或方法体来确保没有中断。在

我是Python(APSW)的替代SQLite包装器的作者,对这个问题非常熟悉。SQLite本身曾经要求对象-数据库连接和游标只能在同一个线程中使用。在SQLite 3.5左右,这一点已经改变了,您可以并发地使用对象,尽管SQLite内部有自己的锁定,所以您实际上并没有获得并发性能。默认的pythonsqlite包装器(aka pysqlite)甚至支持SQLite 3的旧版本,因此它会继续执行此限制,即使SQLite本身不再需要它。但是,pysqlite代码需要修改以允许并发,因为它包装SQLite的方式不安全—例如,处理错误消息是不安全的,因为SQLite API设计缺陷,需要特殊处理。在

注意,光标非常便宜。不要试图重复使用它们或将它们视为珍贵。实际的底层SQLite对象(sqlite3_stmt)保存在缓存中,并根据需要重用。在

如果您想要最大的并发性,那么打开多个连接并同时使用它们。在

APSW文档有更多关于multi-threading and re-entrancy的内容。注意,它有额外的代码来允许pysqlite没有的实际并发使用,但是其他提示和信息适用于SQLite的任何使用。在

相关问题 更多 >