如何在守护程序线程中关闭sqlite连接?

时间:2016-07-24 05:39:22

标签: python multithreading python-2.7 sqlite python-multithreading

我有多个处理数据并将其放在队列中的线程,以及从队列中获取数据然后将其保存到数据库的单个线程。

我认为以下内容会导致内存泄漏:

class DBThread(threading.Thread):
    def __init__(self, myqueue):
        threading.Thread.__init__(self)
        self.myqueue = myqueue

    def run(self):
        conn = sqlite3.connect("test.db")
        c = conn.cursor()

        while True:
            data = myqueue.get()
            if data:
                c.execute("INSERT INTO test (data) VALUES (?)", (data,))
                conn.commit()

            self.myqueue.task_done()

        #conn.close()    <--- never reaches this point

q = Queue.Queue()

# Create other threads
....

# Create DB thread
t = DBThread(q)
t.setDaemon(True)
t.start()

q.join()

我无法将conn.close()置于while循环中,因为我认为这会关闭第一个循环上的连接。我无法将其放在if data:语句中,因为它不会保存可能稍后放入队列的数据。

我在哪里关闭数据库连接?如果我不关闭它,不会导致内存泄漏吗?

1 个答案:

答案 0 :(得分:0)

如果您可以使用不会出现在普通数据中的标记值,例如None,您可以通过finally子句指示线程停止并关闭数据库连接:

import threading
import Queue
import sqlite3

class DBThread(threading.Thread):
    def __init__(self, myqueue, db_path):
        threading.Thread.__init__(self)
        self.myqueue = myqueue
        self.db_path = db_path

    def run(self):
        conn = sqlite3.connect(self.db_path)

        try:
            while True:
                data = self.myqueue.get()    
                if data is None:    # check for sentinel value
                    break

                with conn:
                    conn.execute("INSERT INTO test (data) VALUES (?)", (data,))
                self.myqueue.task_done()
        finally:
            conn.close()


q = Queue.Queue()
for i in range(100):
    q.put(str(i))

conn = sqlite3.connect('test.db')
conn.execute('create table if not exists test (data text)')
conn.close()

t = DBThread(q, 'test.db')
t.start()

q.join()
q.put(None)    # tell database thread to terminate

如果你不能使用sentinel值,你可以在while循环中检查的类中添加一个标志。还要为设置标志的线程类添加stop()方法。您需要使用非阻止Queue.get()

class DBThread(threading.Thread):
    def __init__(self, myqueue, db_path):
        threading.Thread.__init__(self)
        self.myqueue = myqueue
        self.db_path = db_path
        self._terminate = False

    def terminate(self):
        self._terminate = True

    def run(self):
        conn = sqlite3.connect(self.db_path)

        try:
            while not self._terminate:
                try:
                    data = self.myqueue.get(timeout=1)
                except Queue.Empty:
                    continue

                with conn:
                    conn.execute("INSERT INTO test (data) VALUES (?)", (data,))
                self.myqueue.task_done()
        finally:
            conn.close()

....
q.join()
t.terminate()    # tell database thread to terminate

最后,值得一提的是,如果db线程设法耗尽队列,即q.join()返回,则程序可能会终止。这是因为db线程是一个守护程序线程,不会阻止主线程退出。您需要确保您的工作线程产生足够的数据以保持数据库线程忙,否则q.join()将返回并且主线程将退出。