MST

星途 面试题库

面试题:Python复杂代码的审查与潜在错误分析

考虑一个使用多线程和数据库交互的Python项目,以下是一个简化的代码结构,其中包含数据库连接池的使用以及多线程任务调度。请全面审查代码,指出潜在的内存泄漏、资源竞争、数据库连接异常等严重错误,并提供优化方案。 ```python import threading import queue import sqlite3 class DatabaseConnectionPool: def __init__(self, max_connections): self.max_connections = max_connections self.pool = queue.Queue(maxsize = max_connections) for _ in range(max_connections): self.pool.put(sqlite3.connect('example.db')) def get_connection(self): return self.pool.get() def return_connection(self, conn): self.pool.put(conn) pool = DatabaseConnectionPool(5) def task(): conn = pool.get_connection() cursor = conn.cursor() cursor.execute('SELECT * FROM some_table') result = cursor.fetchall() pool.return_connection(conn) threads = [] for _ in range(10): t = threading.Thread(target = task) threads.append(t) t.start() for t in threads: t.join() ```
34.2万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试

潜在问题

  1. 资源竞争
    • 虽然使用了连接池来管理数据库连接,但如果多个线程同时调用get_connectionreturn_connection方法,queue.Queue本身是线程安全的,所以这里不会出现资源竞争问题。但如果task函数中的数据库操作耗时较长,会导致连接长时间被占用,其他线程等待连接的时间过长。
  2. 数据库连接异常
    • task函数中,没有对数据库操作可能出现的异常进行处理。例如,SELECT * FROM some_table语句执行时,如果some_table不存在,会抛出sqlite3.OperationalError异常,而当前代码没有捕获这个异常,这可能导致线程异常终止,并且数据库连接没有被正确返回连接池,造成连接泄漏。
  3. 内存泄漏
    • 如果线程在执行task函数过程中因为未处理的异常而终止,数据库连接可能无法正确返回到连接池,导致内存泄漏。例如,在cursor.execute('SELECT * FROM some_table')执行失败后,conn连接没有被正确地return_connection,后续可能无法再使用该连接,也无法释放相关资源。

优化方案

  1. 异常处理
    • task函数中添加异常处理,确保无论数据库操作是否成功,连接都能正确返回到连接池。
import threading
import queue
import sqlite3


class DatabaseConnectionPool:
    def __init__(self, max_connections):
        self.max_connections = max_connections
        self.pool = queue.Queue(maxsize = max_connections)
        for _ in range(max_connections):
            self.pool.put(sqlite3.connect('example.db'))

    def get_connection(self):
        return self.pool.get()

    def return_connection(self, conn):
        self.pool.put(conn)


pool = DatabaseConnectionPool(5)


def task():
    conn = pool.get_connection()
    try:
        cursor = conn.cursor()
        cursor.execute('SELECT * FROM some_table')
        result = cursor.fetchall()
    except sqlite3.OperationalError as e:
        print(f"数据库操作异常: {e}")
    finally:
        pool.return_connection(conn)


threads = []
for _ in range(10):
    t = threading.Thread(target = task)
    threads.append(t)
    t.start()

for t in threads:
    t.join()
  1. 连接池管理优化
    • 可以考虑添加连接池的健康检查机制,例如定期检查连接池中连接的有效性。如果连接长时间未使用或者已经断开,可以重新创建连接,确保连接池中的连接始终可用。
    • 还可以考虑使用更成熟的数据库连接池库,如DBUtils,它提供了更丰富的功能和更好的性能优化,例如连接池的自动回收、连接池大小动态调整等功能。
from dbutils.pooled_db import PooledDB
import threading


def task():
    conn = pool.connection()
    try:
        cursor = conn.cursor()
        cursor.execute('SELECT * FROM some_table')
        result = cursor.fetchall()
    except sqlite3.OperationalError as e:
        print(f"数据库操作异常: {e}")
    finally:
        conn.close()


# 创建连接池
pool = PooledDB(sqlite3, 5, database='example.db')
threads = []
for _ in range(10):
    t = threading.Thread(target = task)
    threads.append(t)
    t.start()

for t in threads:
    t.join()