我应该如何处理在Python中访问sqlite数据库的多个线程?

我应该如何处理在Python中访问sqlite数据库的多个线程?,python,multithreading,sqlite,Python,Multithreading,Sqlite,SQLite似乎从3.7.13开始就支持多线程访问,Python的sqlite3模块从3.4开始就支持多线程访问。要使用它,您可以编写如下代码: import sqlite3 dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared" connection = sqlite3.connect(dburi, uri=True, check_same_thread=False) with connection: cursor =

SQLite似乎从3.7.13开始就支持多线程访问,Python的
sqlite3
模块从3.4开始就支持多线程访问。要使用它,您可以编写如下代码:

import sqlite3
dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared"
connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
with connection:
    cursor = conneciton.cursor()
    cursor.execute("SQL")
import threading
import sqlite3
dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared"
connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
# NOTE: You'll need to share this same lock object with every other thread using the database
lock = threading.Lock()
with lock:
    with connection:
        cursor = connection.cursor()
        cursor.execute("SQL")
        connection.commit()
import threading
import sqlite3

class LockableSqliteConnection(object):
    def __init__(self, dburi):
        self.lock = threading.Lock()
        self.connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
        self.cursor = None
    def __enter__(self):
        self.lock.acquire()
        self.cursor = self.connection.cursor()
        return self
    def __exit__(self, type, value, traceback):
        self.lock.release()
        self.connection.commit()
        if self.cursor is not None:
            self.cursor.close()
            self.cursor = None
这是可行的,但您首先会发现,您需要锁定对数据库的访问,否则其他线程可能会损坏您的数据。可能是这样的:

import sqlite3
dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared"
connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
with connection:
    cursor = conneciton.cursor()
    cursor.execute("SQL")
import threading
import sqlite3
dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared"
connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
# NOTE: You'll need to share this same lock object with every other thread using the database
lock = threading.Lock()
with lock:
    with connection:
        cursor = connection.cursor()
        cursor.execute("SQL")
        connection.commit()
import threading
import sqlite3

class LockableSqliteConnection(object):
    def __init__(self, dburi):
        self.lock = threading.Lock()
        self.connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
        self.cursor = None
    def __enter__(self):
        self.lock.acquire()
        self.cursor = self.connection.cursor()
        return self
    def __exit__(self, type, value, traceback):
        self.lock.release()
        self.connection.commit()
        if self.cursor is not None:
            self.cursor.close()
            self.cursor = None
现在,如果一个线程获取了锁,那么另一个线程在第一个线程关闭它之前无法获取锁,并且只要所有线程都使用相同的
lock
对象,并记住在使用连接
之前使用lock:
,您的数据就不会被损坏

但是,现在我需要一种通过连接传递锁的方法。您可以使用单独的参数,也可以使用自定义类:

import threading
import sqlite3

class LockableSqliteConnection(object):
    def __init__(self, dburi):
        self.lock = threading.Lock()
        self.connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)

dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared"
lsc = LockableSqliteConnection(dburi)
with lsc.lock:
    with lsc.connection:
        cursor = lsc.connection.cursor()
        cursor.execute("SQL")
        lsc.connection.commit()

这很好,因为类的名称让我想起了我所拥有的,所以至少我不太可能忘记锁并损坏我的数据。但是有没有办法用语句消除这两个
?理想情况下,我希望将它们与
组合成一个单独的
,因为我无论如何都不应该使用没有锁的连接。

在本文的帮助下,我能够编写满足我需要的东西:

我的代码如下所示:

import sqlite3
dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared"
connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
with connection:
    cursor = conneciton.cursor()
    cursor.execute("SQL")
import threading
import sqlite3
dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared"
connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
# NOTE: You'll need to share this same lock object with every other thread using the database
lock = threading.Lock()
with lock:
    with connection:
        cursor = connection.cursor()
        cursor.execute("SQL")
        connection.commit()
import threading
import sqlite3

class LockableSqliteConnection(object):
    def __init__(self, dburi):
        self.lock = threading.Lock()
        self.connection = sqlite3.connect(dburi, uri=True, check_same_thread=False)
        self.cursor = None
    def __enter__(self):
        self.lock.acquire()
        self.cursor = self.connection.cursor()
        return self
    def __exit__(self, type, value, traceback):
        self.lock.release()
        self.connection.commit()
        if self.cursor is not None:
            self.cursor.close()
            self.cursor = None
此类的对象现在可直接用于
with
语句:

dburi = "file:TESTING_MEMORY_DB?mode=memory&cache=shared"
lsc = LockableSqliteConnection(dburi)
with lsc:
    lsc.cursor.execute("SQL")

它还方便地为我打开一个游标,并自动提交我对数据库所做的更改,这意味着调用我的新类的代码更短。这很好,但是自动提交数据库更改实际上可能不是最好的主意。。。在更复杂的场景中,您可能希望启动事务,然后中途回滚,该功能可能会带来麻烦。然而,我的SQL需求非常简单,而且我从不回滚,所以我现在将其留在项目中。

尝试了代码,似乎效果很好。尚未对其进行全面测试,但确实发现了一个问题。self.lock.release()应该是“\uexit”函数的最后一行。否则,在建立新连接时会释放锁,从而导致新连接的光标设置为“无”。