3个版本 (破坏性更新)

0.3.0 2021年2月16日
0.2.0 2019年8月17日
0.1.0 2019年8月17日

#1219 in 数据结构

MIT/Apache

23KB
434

swimmer

Rust的线程安全对象池。

use swimmer::Pool;

let pool: Pool<String> = Pool::with_size(10);
assert_eq!(pool.size(), 10);

let value = pool.get()
assert_eq!(pool.size(), 9);
assert_eq!(*value, "");

drop(value);
// Value is returned to pool
assert_eq!(pool.size(), 10);

更多信息请参阅文档


lib.rs:

Rust的线程安全对象池。

对象池用于重用对象而不重新分配它们。当从池中请求对象时,它将被取出池外;一旦它被丢弃,它将被返回池中,并可以再次检索。

本软件包的主要类型是Pool结构体,它实现了一个线程安全对象池。它可以存储实现了Recyclable特质的对象,该特质允许池初始化和“回收”对象。

其实现如下

  • 使用builder函数创建一个池。它使用初始大小进行配置。
  • 创建池时,池使用Recyclablenew函数初始化initial_size个值。
  • 当从池中请求值时,通常使用Pool::get(),从内部缓冲区中取出一个值。如果没有剩余的值,将使用Recyclable::new()初始化一个新的对象。
  • 然后可以由调用者使用该值。
  • 当值被丢弃时,它将被返回到池中,并且未来的Pool::get()调用可能会返回相同的对象。

为了确保对象被清理,池在将对象返回池之前,会调用对象上的Recyclable::recycle()。此函数移除了对象的任何已更改状态,实际上“重置”了它。例如,请参阅以下事件序列

  • 初始化一个向量池。
  • 从池中检索一个向量,并向其中添加一些值。
  • 向量被丢弃并返回池中。

在不重置向量的情况下,未来对 Pool::get 的调用可能会返回包含那些旧元素的向量;显然,这不是期望的结果。因此,为 Recyclable 实现的 Vec 在回收时清空向量。

该软件包在很大程度上基于 lifeguard 软件包,但它线程安全,而 lifeguard 不是。

线程安全

Pool 是线程安全的,它可以在线程之间共享或在懒加载的静态变量中使用(请参阅示例)。

目前,这是通过使池为每个线程包含一个线程局部缓冲区来实现的,这已经在基准测试中被证明比使用锁定 Veccrossbeam::SegQueue 高出两倍多。

供应商

在某些情况下,您可能需要指定自己的函数来初始化新对象,而不是使用默认的 Recyclable::new() 函数。在这种情况下,您可以选择使用 PoolBuilder::with_supplier(),这将使池使用提供的闭包来初始化新值。

例如,对 Recyclable 的实现为 Vec<T> 分配了一个零容量的向量,但您可能想要给向量一个初始容量。在这种情况下,您可以这样做,例如

use swimmer::Pool;
let pool: Pool<Vec<u32>> = swimmer::builder()
    .with_supplier(|| Vec::with_capacity(128))
    .build();

let vec = pool.get();
assert_eq!(vec.capacity(), 128);

请注意,然而,供应商函数仅在对象首次初始化时被调用:它不会被用于回收对象。这意味着目前没有实现自定义回收功能的方法。

软件包功能

  • hashbrown-impls:为 hashbrown::HashMaphashbrown::HashSet 实现 Recyclable
  • smallvec-impls:为 SmallVec 实现 Recyclable

示例

基本用法

use swimmer::Pool;

// Initialize a new pool, allocating
// 10 empty values to start
let pool: Pool<String> = swimmer::builder()
    .with_starting_size(10)
    .build();

assert_eq!(pool.size(), 10);

let mut string = pool.get();
assert_eq!(*string, ""); // Note that you need to dereference the string, since it is stored in a special smart pointer
string.push_str("test"); // Mutate the string

// One object was taken from the pool,
// so its size is now 9
assert_eq!(pool.size(), 9);

// Now, the string is returned to the pool
drop(string);

assert_eq!(pool.size(), 10);

// Get another string from the pool. This string
// could be the same one retrieved above, but
// since the string is cleared before returning
// into the pool, it is now empty. However, it
// retains any capacity which was allocated,
// which prevents additional allocations
// from occurring.
let another_string = pool.get();
assert_eq!(*another_string, "");

在您的对象上实现 Recyclable

use swimmer::{Pool, Recyclable};

struct Person {
    name: String,
    age: u32,
}

impl Recyclable for Person {
    fn new() -> Self {
        Self {
            name: String::new(),
            age: 0,
        }
    }

    fn recycle(&mut self) {
        // You are responsible for ensuring
        // that modified `Person`s get reset
        // before being returned to the pool.
        // Otherwise, the object could be put
        // back into the pool with its old state
        // still intact; this could cause weird behavior!
        self.name.clear();
        self.age = 0;
     }
}

let pool: Pool<Person> = Pool::new();
let mut josh = pool.get();
josh.name.push_str("Josh"); // Since `recycle` empties the string, this will effectively set `name` to `Josh`
josh.age = 47;

drop(josh); // Josh is returned to the pool and his name and age are reset

// Now get a new person
let another_person = pool.get();

lazy_static 变量中使用 Pool 对象,使其可以在全局范围内使用

use lazy_static::lazy_static;
use swimmer::Pool;

lazy_static! {
    static ref POOL: Pool<String> = {
        Pool::new()
    };
}

let value = POOL.get();

依赖项

~43–290KB