再议 Send,Sync

问题起因


想要通过 trait object 调用 Trait 中定义的 async method(s) (async fn 通过 async-trait 模拟实现)

示例代码如下

use async_std::task;
use async_trait::async_trait;

#[async_trait]
trait Foo {
    
    
    async fn foo(&self);
}

struct A;

#[async_trait]
impl Foo for A {
    
    
    async fn foo(&self) {
    
    
        println!("foo A")
    }
}

fn main() {
    
    
    task::block_on(async {
    
    
        task::spawn(async {
    
    
            let a = A;
            // 直接通过 A 调用 foo 没有问题
            a.foo().await;
            let o = &a as &dyn Foo;
            // 通过 trait object 编译错误如下
            o.foo().await;
        }).await
    });
}

编译错误如下

error: future cannot be sent between threads safely
  --> examples/example.rs:20:9
   |
20 |         task::spawn(async {
    
    
   |         ^^^^^^^^^^^ future created by async block is not `Send`
   | 
  ::: /Users/dian/.cargo/registry/src/mirrors.ustc.edu.cn-61ef6e0cd06fb9b8/async-std-1.6.3/src/task/spawn.rs:28:29
   |
28 |     F: Future<Output = T> + Send + 'static,
   |                             ---- required by this bound in `async_std::task::spawn::spawn`
   |
   = help: the trait `std::marker::Sync` is not implemented for `dyn Foo`
note: future is not `Send` as this value is used across an await
  --> examples/example.rs:24:13
   |
23 |             let o = &a as &dyn Foo;
   |                 - has type `&dyn Foo` which is not `Send`
24 |             o.foo().await;
   |             ^^^^^^^^^^^^^ await occurs here, with `o` maybe used later
25 |         }).await
   |         - `o` is later dropped here

error: aborting due to previous error

错误原因分析


回过头来看错误提示非常明显

the trait std::marker::Sync is not implemented for dyn Foo
note: future is not Send as this value is used across an await

由于 trait object 没有 impl Sync Trait 导致了 o.foo() 返回的匿名 Future 不满足 Send 约束,进而无法在 async_std::task::spawn 中调用 await

理解问题的关键在于理解这句话

对于任意类型 T,如果 &T(T 的引用)是 Send 的话 T 就是 Sync 的

对应代码规则见 Sync 的定义

mod impls {
    
    
    #[stable(feature = "rust1", since = "1.0.0")]
    unsafe impl<T: Sync + ?Sized> Send for &T {
    
    }
    #[stable(feature = "rust1", since = "1.0.0")]
    unsafe impl<T: Send + ?Sized> Send for &mut T {
    
    }
}

对于我们这里来说就是,想要 &dyn Foo 满足 : Send 约束则 dyn Foo 需要满足 : Sync,修改类型转换代码如下

let o = &a as &(dyn Foo + Sync);

问题延伸


问题的根本原因在于有 !Send 的值跨越 await 导致生成的匿名 Future 也是 !Send 继而无法在线程之间传递

如果某结构体内部存在 !Sync 的字段,那么跨越 await 依然有问题,如下所示

struct C(Mutex<RefCell<i32>>);
impl C {
    
    
    async fn bar(&self) {
    
    
        println!("bar C");
    }
}
#[async_trait]
impl Foo for C {
    
    
    async fn foo(&self) {
    
    
        let mut mg = self.0.lock().expect("lock");
        let c = mg.get_mut();
        // 跨越 await 则无法编译通过
        // self.bar().await;
        *c = 10;
    }
}

猜你喜欢

转载自blog.csdn.net/DAOSHUXINDAN/article/details/108311793