← 中文资源

简体中文版本

泛型 RAII 对象池

用互斥锁和 Arc 复用昂贵对象,守卫在离开作用域时自动归还,池空时调用工厂创建新对象。

阅读英文原文

功能说明

数据库连接、编码缓冲区和解析器初始化成本较高,频繁创建销毁会拖慢服务。对象池预先或按需创建对象,借出后由调用方使用,归还后供下一次复用。Rust 的所有权体系适合用 RAII 表达借还:acquire 返回守卫,守卫实现 Deref 与 DerefMut,使用起来像普通引用;离开作用域时 Drop 自动把对象放回池中,即使发生 panic 也能归还。

本例的池是泛型的,工厂函数负责创建新对象。内部用 Arc 共享池状态,互斥锁保护空闲对象列表,因此可以跨线程克隆使用。main 借用字符串缓冲区写入内容,释放后再次借出同一个对象,验证内容被复用。

关键代码

PoolInner 保存空闲列表和工厂 trait 对象,工厂约束为 Fn 加 Send 加 Sync,保证可跨线程调用。acquire 在锁内弹出对象,池空时在锁内调用工厂;若创建过程较慢,可以改为锁外创建以减少阻塞。PoolGuard 用 Option 保存对象,Drop 时 take 并加锁归还,锁中毒时静默丢弃对象,避免 panic 传播。Deref 与 DerefMut 让守卫直接调用 T 的方法。main 用内层作用域显式触发归还。

使用注意

归还前必须把对象恢复到可用状态,例如清空缓冲区、回滚未提交事务或重置协议状态,否则下一次借用会读到脏数据。池没有容量上限,瞬间大量借用后归还会长期占用内存,应设置最大空闲数并在归还时丢弃多余对象。Drop 中不能执行可能 panic 的操作。对象跨线程使用时 T 必须满足 Send,当前代码未显式加约束,编译器会在实际跨线程时检查。池不负责健康检查,失效连接应在借出时验证。

验证方式

运行脚本,输出 reused=true available=0,说明第一次借用的字符串缓冲区被归还后再次借出,且内容仍然存在。把内层作用域去掉,available 会不同,可观察借用周期对空闲数量的影响。把工厂改为计数闭包,可验证池空时才创建新对象。

代码示例

use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};

struct PoolInner<T> {
    items: Mutex<Vec<T>>,
    factory: Box<dyn Fn() -> T + Send + Sync>,
}

struct Pool<T> {
    inner: Arc<PoolInner<T>>,
}

struct PoolGuard<T> {
    inner: Arc<PoolInner<T>>,
    item: Option<T>,
}

impl<T> Pool<T> {
    fn new<F>(factory: F) -> Self
    where
        F: Fn() -> T + Send + Sync + 'static,
    {
        Self {
            inner: Arc::new(PoolInner {
                items: Mutex::new(Vec::new()),
                factory: Box::new(factory),
            }),
        }
    }

    fn acquire(&self) -> PoolGuard<T> {
        let item = self
            .inner
            .items
            .lock()
            .expect("pool mutex poisoned")
            .pop()
            .unwrap_or_else(|| (self.inner.factory)());
        PoolGuard {
            inner: Arc::clone(&self.inner),
            item: Some(item),
        }
    }

    fn available(&self) -> usize {
        self.inner
            .items
            .lock()
            .expect("pool mutex poisoned")
            .len()
    }
}

impl<T> Deref for PoolGuard<T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.item.as_ref().expect("guard item is present")
    }
}

impl<T> DerefMut for PoolGuard<T> {
    fn deref_mut(&mut self) -> &mut T {
        self.item.as_mut().expect("guard item is present")
    }
}

impl<T> Drop for PoolGuard<T> {
    fn drop(&mut self) {
        if let Some(item) = self.item.take() {
            if let Ok(mut items) = self.inner.items.lock() {
                items.push(item);
            }
        }
    }
}

fn main() {
    let pool = Pool::new(String::new);
    {
        let mut first = pool.acquire();
        first.push_str("hello");
    }
    let second = pool.acquire();
    println!(
        "reused={} available={}",
        !second.is_empty(),
        pool.available()
    );
}