4 个版本
0.2.0 | 2023 年 12 月 31 日 |
---|---|
0.1.3 | 2023 年 12 月 21 日 |
0.1.2 | 2023 年 12 月 19 日 |
0.1.1 | 2023 年 12 月 18 日 |
0.1.0 |
|
248 在 并发 中
每月 39 次下载
100KB
2K SLoC
锁
锁是一个向下计数器,可用于同步线程或协调任务。计数器的值在创建时初始化。线程/任务可能会在锁上阻塞/暂停,直到计数器减到 0。
与 std::sync::Barrier
相比,它是一个一次性现象,即计数器达到 0 后将不会重置。相反,它具有有用的属性,即它不会通过调用 count_down()
或 arrive()
使它们等待计数器达到 0。这也意味着它可以由参与线程/任务多次减少。
另请参阅 C++ 20 中的 std::latch
,Java 中的 java.util.concurrent.CountDownLatch
,以及 Concurrent::CountDownLatch
。
快速入门
默认使用 atomic-wait
的 sync
实现
cargo add latches
使用 std
的 task
实现
cargo add latches --no-default-features --features task --features std
的 futex
实现
cargo add latches --no-default-features --features futex
另请参阅 应该使用哪一个?
当未启用 std
功能时,它可以与 no_std
一起使用。
用法
等待完成
use std::{sync::Arc, thread};
// Naming rule: `latches::{implementation-name}::Latch`.
use latches::sync::Latch;
let latch = Arc::new(Latch::new(10));
for _ in 0..10 {
let latch = latch.clone();
thread::spawn(move || latch.count_down());
}
// Waits 10 threads complete their works.
// Requires `.await` if it is the `task` implementation.
latch.wait();
门
use std::{sync::Arc, thread};
// Naming rule: `latches::{implementation-name}::Latch`.
use latches::sync::Latch;
let gate = Arc::new(Latch::new(1));
for _ in 0..10 {
let gate = gate.clone();
thread::spawn(move || {
// Waits for the gate signal.
// Requires `.await` if it is the `task` implementation.
gate.wait();
// Do some work after gate.
});
}
// Allows 10 threads start their works.
gate.count_down();
实现
同步
sync
实现是线程的默认实现。
功能依赖
- 添加
std
功能将使其使用std::sync::Mutex
和std::sync::Condvar
作为条件变量,它支持超时。 - 如果禁用了
std
,则添加atomic-wait
功能将使其使用atomic-wait
作为条件变量,它不支持超时。 - 如果同时禁用了
std
和atomic-wait
,则会在编译时抛出错误。
默认功能中同时启用了 atomic-wait
和 sync
,以便于使用。因此,如果您想使用 sync
与 std
并且不想导入不必要的包 atomic-wait
,请禁用默认功能。
Futex
futex
的实现类似于 C++20 中流行的实现 std::latch
,与 sync
实现相比,提供了略微更好的性能。
它不支持等待时的超时。
功能依赖
- 它依赖于功能
atomic-wait
和包atomic-wait
,并且不能被禁用。
任务
task
的实现通常用于协调异步任务。
如果在 no_std
中,它需要 extern crate alloc
。
功能依赖
- 添加
std
功能将使其在唤醒器收集时使用std::sync::Mutex
作为线程互斥锁。 - 如果禁用了
std
,则添加atomic-wait
功能将使其在唤醒器收集时使用atomic-wait
作为线程互斥锁。 - 如果同时禁用了
std
和atomic-wait
,则它将在唤醒器收集时使用自旋锁作为线程互斥锁。
相似之处与不同之处
相似之处
- 所有实现都基于原子操作,因此在不支持原子的平台上无法工作,即
#[cfg(target_has_atomic)]
- 如果未启用
std
功能,则所有实现都可以在no_std
上使用。 - 除了等待之外的所有方法:不需要
async
/await
不同之处
sync |
task |
futex |
|
---|---|---|---|
计数器类型 | usize |
usize |
u32 |
互斥锁 | std 或 atomic-wait |
std 、atomic-wait 或自旋锁 |
无互斥锁* |
等待 | 阻塞 | 未来 | 阻塞 |
超时 | 需要 std |
需要异步定时器 | 不支持 |
* 无互斥锁并不意味着它不需要消除竞态条件,实际上它使用 Futex(即 atomic-wait)代替。
应该使用哪一个?
如果您的项目使用 async
/await
任务,请使用 task
实现。您可以通过添加 std
或 atomic-wait
功能使其在 门控 场景中具有更高的并发友好性。如下所示:
cargo add latches --no-default-features --features task --feature atomic-wait
如果您的项目中并发量较小,请使用 futex
实现。如下所示:
cargo add latches --no-default-features --features futex
否则,请使用 sync
实现。它与 task
实现具有相同的计数器类型 usize
,以及 std::sync::Barrier
。添加 std
功能将使其支持超时。请注意,它应该与 std
或 atomic-wait
功能之一一起使用,否则将抛出编译错误。如下所示:
# Both `sync` and `atomic-wait` are enabled in default features
cargo add latches
或启用 std
功能以支持超时
cargo add latches --no-default-features --features sync --features std
在高并发情况下,futex
实现和 sync
实现之间没有明显的性能差距。
此外,如果您正在将 C++ 代码迁移到 Rust,使用 futex
实现可能是一种更加保守的方法,例如,类似内存使用、ABI 调用等。请注意,futex
实现没有未定义的行为,这与 C++ 中的 std::latch
不同。
性能
运行基准测试
使用带有 atomic-wait
的所有实现运行基准测试(futex
实现依赖于 atomic-wait
)
cargo bench --package benches
使用带有 std
的 sync
实现运行基准测试
cargo bench --package benches --no-default-features --features sync --features std
使用带有 atomic-wait
的 task
实现运行基准测试
cargo bench --package benches --no-default-features --features task --features atomic-wait
或使用 std
和比较组运行基准测试
cargo bench --package benches --features std --features comparison
等等。
总体基准测试包括线程调度开销,而 Latch
的速度比线程调度快得多,因此可能存在时间抖动和较大的标准偏差。所有总体基准测试都将有后缀 -overall
。
异步比较组也是基于原子的,并依赖于特定的异步库,如 tokio
和 async-std
。
同步比较组使用 Mutex
状态而不是原子。
许可
Latches 根据 MIT 许可证或 Apache 许可证版本 2.0 发布,由您选择。
依赖项
~0–11MB
~58K SLoC