我有多个处理数据并将其放在队列中的线程,以及从队列中获取数据然后将其保存到数据库的单个线程。
我认为以下内容会导致内存泄漏:
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:
语句中,因为它不会保存可能稍后放入队列的数据。
我在哪里关闭数据库连接?如果我不关闭它,不会导致内存泄漏吗?
答案 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()
将返回并且主线程将退出。