python中单例模式应用

[复制链接]
发表于 2025-10-23 10:44:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
数据库毗连池单例模式
 


1. 为什么使用单例模式

创建数据库毗连是一个昂贵的过程(涉及网络通讯、认证等)。单例模式的毗连池可以在步调启动时初始化一组毗连,并在整个生命周期中重用这些毗连,而不是每次哀求都新建毗连。同时还可以控制毗连数量,防止资源耗尽。

2.代码


下面这段代码实现了一个数据库毗连池,而且通过单例模式确保整个步调中只有一个毗连池实例,包罗扩容机制。克制频仍创建和烧毁数据库毗连,通过单例复用毗连池:
通过单例模式确保全局只有一个毗连池实例。
使用线程锁和条件变量实现线程安全,处置处罚多线程环境下的并发访问。
提供毗连的创建、获取、归还和关闭功能。
通过多线程测试验证毗连池的并发性能。
  1. import pymysql
  2. import threading
  3. from pymysql import Error
  4. class DatabasePool:
  5.     _instance = None
  6.     def __new__(cls):
  7.         if not cls._instance:
  8.             cls._instance = super().__new__(cls)
  9.             # 数据库配置
  10.             cls._instance.config = {
  11.                 'host': 'localhost',
  12.                 'user': 'root',
  13.                 'password': '111111',
  14.                 'database': 'test',
  15.                 'charset': 'utf8mb4',
  16.                 'cursorclass': pymysql.cursors.DictCursor
  17.             }
  18.             # 使用可重入锁和条件变量
  19.             cls._instance.lock = threading.RLock()
  20.             cls._instance.condition = threading.Condition(cls._instance.lock)
  21.             cls._instance.max_connections = 10
  22.             cls._instance.connections = []  # 未使用的连接
  23.             cls._instance.in_use = set()   # 正在使用的连接
  24.             cls._instance.init_pool()
  25.         return cls._instance    # 返回唯一的连接池实例
  26.     def init_pool(self):
  27.         """初始化连接池(线程安全)"""
  28.         with self.lock:
  29.             for _ in range(self.max_connections):
  30.                 self.add_connection()
  31.     def create_connection(self):
  32.         """创建单个数据库连接(无需加锁)"""
  33.         try:
  34.             conn = pymysql.connect(**self.config)
  35.             print(f"成功创建连接:{conn._sock.getsockname()}")
  36.             return conn
  37.         except Error as e:
  38.             print(f"连接创建失败: {e}")
  39.             return None
  40.     def add_connection(self):
  41.         """向连接池添加连接(线程安全)"""
  42.         if len(self.connections) + len(self.in_use) < self.max_connections:
  43.             conn = self.create_connection()
  44.             if conn:
  45.                 self.connections.append(conn)
  46.     def get_connection(self):
  47.         """获取连接(线程安全)"""
  48.         with self.condition:
  49.             while not self.connections:
  50.                 if len(self.in_use) < self.max_connections:
  51.                     self.add_connection()
  52.                 else:
  53.                     print("连接池已满,等待连接归还...")
  54.                     self.condition.wait()  # 等待连接归还
  55.             conn = self.connections.pop()
  56.             self.in_use.add(conn)
  57.             print(f"获取连接:{conn._sock.getsockname()}")
  58.             return conn
  59.     def release_connection(self, conn):
  60.         """归还连接(线程安全)"""
  61.         with self.condition:
  62.             if conn in self.in_use:
  63.                 self.in_use.remove(conn)
  64.                 if conn.open:
  65.                     self.connections.append(conn)
  66.                     print(f"归还连接:{conn._sock.getsockname()}")
  67.                     self.condition.notify()  # 通知等待的线程
  68.                 else:
  69.                     print("警告:连接已关闭,直接丢弃")
  70.     def close_pool(self):
  71.         """关闭所有连接(线程安全)"""
  72.         with self.lock:
  73.             for conn in self.connections + list(self.in_use):
  74.                 if conn.open:
  75.                     conn.close()
  76.             self.connections.clear()
  77.             self.in_use.clear()
  78.             print("所有数据库连接已关闭")
  79. # 多线程测试示例
  80. if __name__ == "__main__":
  81.     import concurrent.futures
  82.     def worker(thread_id):
  83.         pool = DatabasePool()
  84.         conn = pool.get_connection()
  85.         try:
  86.             with conn.cursor() as cursor:
  87.                 cursor.execute("SELECT SLEEP(1)")  # 模拟耗时操作
  88.                 print(f"线程 {thread_id} 执行查询")
  89.         finally:
  90.             pool.release_connection(conn)
  91.     # 创建连接池
  92.     pool = DatabasePool()
  93.     # 使用15个线程并发测试,超过最大连接数
  94.     with concurrent.futures.ThreadPoolExecutor(max_workers=15) as executor:
  95.         futures = [executor.submit(worker, i) for i in range(15)]
  96.         for future in concurrent.futures.as_completed(futures):
  97.             future.result()
  98.     pool.close_pool()
复制代码
3. 核心:__new__ 方法
  1. class DatabasePool:
  2.     _instance = None  # 类变量,用于保存唯一的实例
  3.     def __new__(cls):
  4.         if not cls._instance:
  5.             cls._instance = super().__new__(cls)
  6.             # 数据库配置
  7.             cls._instance.config = {
  8.                 'host': 'localhost',
  9.                 'user': 'root',
  10.                 'password': '111111',
  11.                 'database': 'test',
  12.                 'charset': 'utf8mb4',
  13.                 'cursorclass': pymysql.cursors.DictCursor
  14.             }
  15.             # 使用可重入锁和条件变量
  16.             cls._instance.lock = threading.RLock()
  17.             cls._instance.condition = threading.Condition(cls._instance.lock)
  18.             cls._instance.max_connections = 10
  19.             cls._instance.connections = []  # 未使用的连接
  20.             cls._instance.in_use = set()   # 正在使用的连接
  21.             cls._instance.init_pool()
  22.         return cls._instance    # 返回唯一的连接池实例
复制代码

  • ​作用:确保无论创建多少次 DatabasePool(),都只会天生同一个实例。
  • ​示例:
    1. pool1 = DatabasePool()  # 第一次创建,初始化连接池
    2. pool2 = DatabasePool()  # 直接返回 pool1 的实例
    3. print(pool1 is pool2)   # 输出 True
    复制代码
3. 总结

我们可以把毗连池想象成一个共享铅笔盒(全班共享):

  • ​锁:管理员(锁)确保一次只有一个人能拿铅笔。
  • ​毗连:铅笔盒里的铅笔(初始10支)。
  • ​动态扩容:当铅笔用完时,管理员临时制作新铅笔。
  • ​归还机制:用完后必须归还,否则其他人无法使用。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表