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

时间:2012-03-05 20:49:09

标签: python object

首先,我不知道“所有权”是否是正确的术语,这正是我在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中是否还有其他术语? (编辑:有关此问题的解释,请阅读主要问题的评论)

我很乐意为这个案例提出具体的建议,但我一般对Python中“什么属于谁”的整个概念更感兴趣,因为对我而言,它似乎与Java处理它的方式完全不同,我计划将来使用Python很多,我现在也可以学习它,因为这是Python非常重要的一部分。

2 个答案:

答案 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的任何使用。