#traits #send #variables #local #performance #constraint #async

orion-async

为了性能,消除异步函数本地变量必须实现 Send Trait 的约束

2 个版本

0.1.1 2023 年 2 月 18 日
0.1.0 2023 年 2 月 17 日

#2210Rust 模式

MIT/Apache

8KB

orion-async

为了性能,消除异步函数本地变量必须实现 Send Trait 的约束

消除异步函数内部本地变量必须实现 Send Trait 的约束,以提高性能。

正常情况下,以下代码无法工作。

use std::rc::Rc;

#[tokio::main]
async fn main() {
    tokio::spawn(foo()).await;
}

async fn foo() {
    let id = Rc::new(100);
    tokio::spawn(bar(*id)).await; 
}

async fn bar(id: i32) {
    println!("bar( {} )", id);
}

以上代码编译会报错

error: future cannot be sent between threads safely
   --> src/main.rs:5:18
    |
5   |     tokio::spawn(foo()).await;
    |                  ^^^^^ future returned by `foo` is not `Send`
    |
    = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<i32>`

仅在异步函数内部使用的 Rc 不会出现并发访问的场景。理想情况下,应该只需要异步函数的输入参数支持 Send Trait 即可,而不应对异步函数内部使用的变量类型有任何约束。

如果用 Arc 替代 Rc,会牺牲性能。orion_async 提供此场景下兼顾安全和性能的解决方案,只需为异步函数 foo 增加如下 orion_async::future 过程宏定义即可。

use std::rc::Rc;

#[tokio::main]
async fn main() {
    tokio::spawn(foo()).await;
}

#[orion_async::future(body_send = true)]
async fn foo() {
    let id = Rc::new(100);
    tokio::spawn(bar(*id)).await; 
}

async fn bar(id: i32) {
    println!("bar( {} )", id);
}

也支持 struct 的 async 方法。

use std::rc::Rc;

#[tokio::main]
async fn main() {
    let foo = Foo;
    let val = tokio::spawn(foo.foo()).await.unwrap();
    println!("val = {}", val);
}

struct Foo;

impl Foo {
    #[orion_async::future(body_send = true)]
    async fn foo(self) -> i32 {
        let val = Rc::new(100);
        tokio::spawn(Foo::bar(*val)).await.unwrap()
    }
    async fn bar(val: i32) -> i32 {
        val + 100
    }
}

使用方法

#[orion_async::future(body_send = true)]
async fn foo() {
   ...
}

此过程宏只能作用于异步函数,且只能消除异步函数内部变量可使用不支持 Send Trait 的数据类型的约束,不会改变函数输入参数的任何约束。

宏名称 属性名 属性值类型 默认值
future
body_send bool false

依赖

~1.5MB
~35K SLoC