1 个不稳定版本

0.1.0 2024 年 4 月 29 日

并发 中排名 #307

Download history 268/week @ 2024-04-26 22/week @ 2024-05-03 51/week @ 2024-05-17 38/week @ 2024-05-24 22/week @ 2024-05-31 5/week @ 2024-06-07 4/week @ 2024-06-14 2/week @ 2024-06-21 81/week @ 2024-06-28 4/week @ 2024-07-05 160/week @ 2024-07-19 13/week @ 2024-07-26 4/week @ 2024-08-02

每月下载量:177

MIT 许可证

11KB
53

dropout

一个小型库,用于将您的值发送到线程以进行丢弃。受 Aaron Abramov 的文章这篇文章defer-drop crate的启发

简单示例

// See examples/demo.rs


type HeavyObject = HashMap<usize, Vec<usize>>;

fn make_heavy_object() -> HeavyObject {
    (1..=NUM_ELEMENTS).map(|v| (v, vec![v])).collect()
}

println!("Allocating a heavy object");
let first_heavy_object = make_heavy_object();

println!("Duplicating that vector");
let second_heavy_object = first_heavy_object.clone();

// Create a dropper, dropping `Vec<Vec<String>>`.
let dropper = dropout::new_dropper();

// This is a special case for this small test.
// The closure will explictly drop vec1 but also implicitly drop the `dropper`
// and we don't want that as, at drop, dropper wait for background thread.
// We don't want to mesure that.
let dropper_clone = dropper.clone();
let dropout_timer = timer(move || dropper_clone.dropout(first_heavy_object));
let std_timer = timer(move || drop(second_heavy_object));

println!("Duration of dropout: {:?}", dropout_timer);
println!("Duration of std drop: {:?}", std_timer);

输出结果(可能是)

Allocating a heavy object
Duplicating that vector
Dropping the vectors
Duration of dropout: 7.479µs
Duration of std drop: 50.864814ms

与 defer-drop 的区别

API

Defer-drop 使用一个全局的后台线程来丢弃任何类型。

  • 所有值都通过 Box 发送到后台线程。
  • 您必须创建一个 DeferDrop,它将包装您的值,并在包装器被丢弃时将其发送到丢弃线程。

Dropout 的相反方向

  • 您创建一个 Dropper,它接受(并获取所有权)一个 T
  • 您有一个线程对应一个 Dropper。
  • 您处理 T 对象,并在结束时显式延迟丢弃。

因此,您操作的是 T 值,而不是 Box<Self>。但您必须明确关于丢弃值。

保证

Defer-drop 不保证值实际上被丢弃,因为主线程可能先于丢弃线程完成所有值的丢弃。 Dropper 等待(因此,在丢弃时阻塞)后台线程完成(并丢弃所有值)之前才会被丢弃。

许可差异

  • defer-drop 根据 MPL-2.0 许可
  • dropout 根据 MIT 许可
// In external library

pub trait MyTrait {
  fn get_u32(&self) -> u32;
}

fn do_stuff_with_object<T: MyTrait>(object: T) {
  ...
  drop(object)
}

// In user library
use external_library::{MyTrait, do_stuff_with_object};

struct MyObject {}
impl MyTrait for MyObject {
  fn get_u32(self) -> u32 {
    5
  }
};

struct DeferedMyObject(DeferedDrop<MyObject>)

impl MyTrait for DeferedMyObject {
  fn get_u32(&self) -> u32 {
    self.0.get_u32()
  }
}

fn main() {
  do_stuff_with_object(MyObject{}); // Works
  do_stuff_with_object(DeferDrop::new(MyObject{})); // Doesn't work as DeferDrop doesn't impl MyTrait
  do_stuff_with_object(DeferedMyObject(DeferDrop::new(MyObject{}))); // Works
}

如果 trait 使用 Box<Self> 消费自身,则几乎不可能做到这一点

pub trait MyTrait {
  fn get_as_u32(self: Box<Self>) -> u32;
}

fn do_stuff_with_u32(object: Box<dyn MyTrait>) {
  let value = object.get_as_u32();
  ...
}

注意

Dropout(类似于 defer-drop)不是万能的。将值发送到另一个线程是昂贵的,并且可能适得其反。在使用此类 crate 之前,请始终进行性能分析。

丢弃的值被排队到一个无界通道中,以便由丢弃线程消费;如果您产生的值多于线程可以处理的值,这会导致无界内存消耗。目前没有方法让线程在过载时发出信号或阻塞。

所有标准非确定性线程注意事项都适用。对象保证按接收的顺序在通道中进行解构,这意味着来自单个线程的对象将按顺序解构。然而,无法保证来自不同线程的交错值的顺序。

此外,无法保证值在丢弃前将被排队多长时间。然而,当丢弃器被丢弃时,它将等待后台线程完成,因此可以保证所有对象最终都会被丢弃。

依赖项

~345KB