21 个版本

0.8.2 2023年3月11日
0.8.1 2022年6月12日
0.7.2 2023年3月11日
0.7.1 2022年5月28日
0.1.1 2017年11月7日

#92并发

Download history 94/week @ 2024-03-11 81/week @ 2024-03-18 55/week @ 2024-03-25 183/week @ 2024-04-01 52/week @ 2024-04-08 105/week @ 2024-04-15 110/week @ 2024-04-22 82/week @ 2024-04-29 101/week @ 2024-05-06 78/week @ 2024-05-13 79/week @ 2024-05-20 75/week @ 2024-05-27 100/week @ 2024-06-03 79/week @ 2024-06-10 84/week @ 2024-06-17 98/week @ 2024-06-24

370 次每月下载
用于 13 个 Crates (9 直接)

Apache-2.0

135KB
2K SLoC

Desync

[dependencies]
desync = "0.8"

Desync 提供了一种新的同步类型,Desync<T>,它通过对其封装的数据类型上的操作进行排序来实现,而不是使用互斥锁来保护关键部分的传统方法。这允许并发围绕两个基本操作构建

  • desync_thing.sync(|thing| /* ... */) 用于同步访问数据
  • desync_thing.desync(|thing| /* ... */) 用于异步访问数据 - 在后台运行提供的任务。

如果只使用 sync() 操作,这大致相当于标准的 Mutex,但提供了更强大的保证,即哪个线程首先获得数据。另一个操作 desync() 有效地替代了在程序中添加并发所需生成线程和移动数据的需求。

Desync还提供了针对异步代码的等效方法:使用future_sync()将在当前异步上下文中执行操作,而使用future_desync()则会在后台调度操作。这些方法可以与sync()desync()操作自由混合,使得混合使用传统线程和异步future的代码变得相对简单。由于Desync使用操作顺序来保证对数据的独占访问,因此这些操作可以在任何可能需要的await之间借用包含的数据,与使用Mutex类型创建的锁不同,这些锁不能在线程之间发送。

Desync提供了相当强的排序保证:特别是,当任何方法返回时,操作顺序相对于任何后续操作是保证的。这种属性使得desync代码易于跟踪,并且比传统线程更不容易发生竞争条件。轻松安排异步更新的能力提供了一种绕过常见场景的方法,在这些场景中,需要锁定多个互斥锁可能会造成死锁。

快速入门

Desync提供了一个单一类型Desync<T>,可以用来替换线程和互斥锁。此类型为包含的数据结构调度操作,确保它们总是按顺序执行,并可选项地后台执行。

可以这样创建一个Desync对象:

use desync::Desync;
let number = Desync::new(0);

它支持两种主要操作。使用desync将为对象调度一个新任务,该任务将在后台线程中运行。这对于延迟长时间运行的操作并将更新移动以便它们可以并行运行非常有用。

let number = Desync::new(0);
number.desync(|val| {
    // Long update here
    thread::sleep(Duration::from_millis(100));
    *val = 42;
});

// We can carry on what we're doing with the update now running in the background

另一个操作是sync,它将任务调度到数据结构上同步运行。这对于从Desync检索值非常有用。

let new_number = number.sync(|val| *val);           // = 42

Desync对象始终以提供的顺序执行操作,因此所有操作都是按包含的数据的视角序列化的。当与执行操作的能力相结合时,这为立即并行化长时间运行的操作提供了一种有用的方法。

与future协同工作

Desync支持futures库。最简单的操作是future_sync(),它创建一个在Desync对象上异步运行的future,但与desync()不同,它可以返回一个结果。它的工作方式如下:

let future_number = number.future_sync(|val| future::ready(*val));
assert!(executor::block_on(async { future_number.await.unwrap() }) == 42 )

还有一个future_desync()操作,当期望线程阻塞时可以使用。它可以用于与future_sync()相同的场景,但它有一个detach()方法来在后台保留任务运行,或者一个sync()方法来等待结果被计算。

Desync 还可以通过 pipe_in()pipe() 函数在后台运行流。这些函数在 Arc<Desync<T>> 引用上工作,并提供了一种异步处理流的方法。这两个函数提供了一种强大的方式来处理输入,并且还可以通过消息传递将 Desync 对象连接起来进行通信。

let some_object = Arc::new(Desync::new(some_object));

pipe_in(Arc::clone(&number), some_stream, 
    |some_object, input| some_object.process(input));

let output_stream = pipe(Arc::clone(&number), some_stream, 
    |some_object, input| some_object.process_with_output(input));

依赖关系

~0.6–0.9MB
~16K SLoC