python: как разделить соединение sqlite между потоками с очередями? - PullRequest
4 голосов
/ 16 июля 2011

Я использую Python 3.2.1 в Arch Linux x86_64.
Я пытаюсь обновить базу данных sqlite в поточном, синхронизированном цикле с помощью кода, подобного следующему:

import sqlite3
from threading import Timer
from queue import Queue

class DBQueue(Queue):
    def give(self, item):
        self.task_done()
        self.join()
        self.put(item)
        return True


def timer():
    print('A')
    Timer(3, add).start()


def add():
    print('B')
    db = qdb.get()
    cur = db.cursor()
    cur.execute('INSERT INTO Foo (id) VALUES (NULL)')
    qdb.give(db)
    timer()

qdb = DBQueue()
# SOLUTION #1:
# qdb.put(sqlite3.connect(':memory:', check_same_thread=False))
# SOLUTION #2: see Eli Bendersky's answer
qdb.put(sqlite3.connect(':memory:'))
db = qdb.get()
cur = db.cursor()
cur.execute('CREATE TABLE Foo (id INTEGER PRIMARY KEY)')
qdb.give(db)
timer()

который, к сожалению, возвращает:

A
B
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python3.2/threading.py", line 736, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.2/threading.py", line 942, in run
    self.function(*self.args, **self.kwargs)
  File "/home/dario/dev/python/prova/src/prova4.py", line 27, in add
    cursor = db.cursor()
sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread.The object was created in thread id 140037302638336 and this is thread id 140037262886656

Совместное использование только курсора не дает лучших результатов:

conn = sqlite3.connect(':memory:')
qdb.put(conn.cursor())

Я совершенно уверен, что совсем не понял, как использовать очереди для обмена базами данных между потоками, кто-нибудь может мне помочь? Спасибо!

1 Ответ

3 голосов
/ 16 июля 2011

Вам не нужно Queue - просто используйте отдельные подключения к одной и той же базе данных из двух потоков. Имейте в виду, что вы не должны ожидать многого с точки зрения упорядочения, когда отдельные соединения передают данные в БД. Рассматривайте это так, как если бы у вас было два разных экземпляра вашей программы, одновременно обращающихся к БД.


Если по какой-то причине вы чувствуете, что должны совместно использовать соединение, попробуйте следующее: поскольку вы столкнулись с проблемой создания объектов SQLite из одного потока и использования их в другом, почему бы не делегировать задачу обработки БД / соединения в одном потоке, и позволить ему общаться с другими через Queue s. Более конкретно:

  • Тема DB_thread: "владеет" соединениями. Получает команды в очереди из других потоков, выполняет их, помещает результаты в «очередь результатов»
  • Потоки A, B, C: передают «команды» в очередь и получают результаты из «очереди результатов».

Обратите внимание, что эти команды не являются объектами SQLite.

...