#thread #同步 #原子操作 #内存 #跨平台 #同步

no-std rendezvous_swap

高效地在线程对之间交换数据和同步执行

1 个不稳定版本

0.1.0 2023年3月16日

#974 in 并发

GPL-3.0 许可证

18KB
142

rendezvous_swap

rendezvous 是线程对之间的执行屏障,但这个 crate 还提供了在同步点交换数据的选项。(术语来自 《信号量小书》

这主要适用于线程频繁同步的情况。与普通的自旋锁不同,它不使用任何 CAS 指令,仅使用 Acquire 加载和 Release 存储,这意味着它可以在 x86_64 上编译为少量非原子指令。因为这个 crate 使用原子操作进行同步,所以它也是 no_std

数据内部以指针进行交换,因此大型结构体交换的成本不高,因此不需要装箱。

在 i5-7200U CPU 上的微基准测试中,交换数据需要不到 100 ns

安全性

RendezvousData 包含 unsafe 但在 Miri 运行时所有测试都通过。

示例:同步线程执行

use rendezvous_swap::Rendezvous;
use std::thread;

let (mut my_rendezvous, mut their_rendezvous) = Rendezvous::new();
thread::spawn(move || {
    for i in 1..5 {
        println!("{i}");
        their_rendezvous.wait();
    }
});
for i in 1..5 {
    println!("{i}");
    my_rendezvous.wait();
}

这将打印

1
1
2
2
3
3
4
4

示例:交换线程数据

use std::thread;
use rendezvous_swap::RendezvousData;

let (mut my_rendezvous, mut their_rendezvous) = RendezvousData::new(0, 0);
let handle = thread::spawn(move || {
    let borrow = their_rendezvous.swap();
    *borrow = 3;

    let borrow = their_rendezvous.swap();
    assert_eq!(7, *borrow);
});
let borrow = my_rendezvous.swap();
*borrow = 7;

let borrowed_data = my_rendezvous.swap();
assert_eq!(3, *borrowed_data);

示例:安全性

以下代码由于 RendezvousData::swap 提供的引用生命周期有限,无法编译。你会得到熟悉的生命周期错误,就像你正在借用结构体元素一样。这个 crate 是安全的,因为两个线程同时拥有对同一内存位置的互斥引用是不可能的。

use std::thread;
use rendezvous_swap::RendezvousData;

let (mut my_rendezvous, mut their_rendezvous) = RendezvousData::new(0, 0);
let handle = thread::spawn(move || {
    their_rendezvous.swap(); // swap return values can be ignored
    their_rendezvous.swap();
});
let old_borrow = my_rendezvous.swap(); // first mutable borrow occurs here

let new_borrow = my_rendezvous.swap(); // second mutable borrow occurs here

*old_borrow = 3; // first borrow is later used here

当前版本:0.1.0

许可证:GPL-3.0

依赖项