美文网首页RUST编程
005 Rust 异步编程,Pin 介绍

005 Rust 异步编程,Pin 介绍

作者: 令狐壹冲 | 来源:发表于2020-07-06 22:08 被阅读0次

    为了对Future调用poll,需要使用到Pin<T>的特殊类型。本节就介绍一下Pin类型。

    异步背后的一些原理

    例子1

    • 源码
    //文件src/main.rs
    use futures::executor;
    
    async fn async_function1() {
        println!("async function1 ++++ !");
    }
    
    async fn async_function2() {
        println!("async function2 ++++ !");
    }
    
    async fn async_main() {
        let f1 = async_function1();
        let f2 = async_function2();
        
        //重点关注这里---------
        let f = async move {
            f1.await;
            f2.await; 
        };
        //---------------------
        f.await;
    }
    
    fn main() {
        executor::block_on(async_main());
    }
    
    • 配置,在Cargo.toml中添加
    [dependencies]
    futures = "0.3.4
    

    我们主要考虑async_main()函数中的async块(async函数也是一样,通过async都是转化为Future),实际背后会做如下工作:
    (1)为async块生成一个类似于如下的结构体:

    struct AsyncFuture {
        fut_one: FutFunction1,
        fut_two: FutFunction2,
        state: State,
    }
    
    //state的定义可能如下
    enum State {
        AwaitingFutFunction1,
        AwaitingFutFunction2,
        Done,
    }
    

    (2)为其生成对应的poll函数:

    impl Future for AsyncFuture {
        type Output = ();
    
        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
            loop {
                match self.state {
                    State::AwaitingFutFunction1 => match self.fut_one.poll(..) {
                        Poll::Ready(()) => self.state = State::AwaitingFutFunction2,
                        Poll::Pending => return Poll::Pending,
                    }
                    State::AwaitingFutFunction2 => match self.fut_two.poll(..) {
                        Poll::Ready(()) => self.state = State::Done,
                        Poll::Pending => return Poll::Pending,
                    }
                    State::Done => return Poll::Ready(()),
                }
            }
        }
    }
    

    好,到这里,我们只是模拟编译器给async代码块进行了展开,那么和我们要讲的Pin有什么关系呢?

    例子2

    我们再考虑如下例子:

    async fn async_put_data_to_buf(mut buf: &[u8]) {
        //to do
        ...
    }
    
    async fn async_main () {
        //重点关注这里---------
        let f = async {
            let mut x = [0; 128];
            let read_into_buf_fut = async_put_data_to_buf(&mut x);
            read_into_buf_fut.await;
        };
        //---------------------
        
        f.await;
    }
    

    对于上面async_main中的async块,编译器为其生成的结构如下:

    struct ReadIntoBuf<'a> {
        buf: &'a mut [u8], // points to `x` below
    }
    
    struct AsyncFuture {
        x: [u8; 128], 
        read_into_buf_fut: ReadIntoBuf<'what_lifetime?>,
    }
    

    在AsyncFuture中,read_into_buf_fut.buf指向x,相当于是一个自引用(一个字段引用另一个字段)。但是如果AsyncFuture发生移动,x肯定也会发生移动,如果read_into_buf_fut.buf还是指向原来的值的话,则会变成无效。
    而Pin就是为了解决此问题的。

    Pin介绍

    Pin类型包着指针类型,保证指针背后的值将不被移动。例如 Pin<&mut T>,Pin<&T>, Pin<Box<T>> 都保证 T 不会移动。

    拿上面的例子来说,如果使用Pin<>就是将x对应的那块内存固定,这样即使AsyncFuture发生移动,但是x不会移动,那么read_into_buf_fut.buf不会变成悬垂引用。

    大多数类型都没有移动的问题,这些类型实现了 Unpin trait。Unpin 类型指针可以自由从 Pin 中放入或取出。例如,u8 就是 Unpin的。

    如果需要Future和Unpin一起使用,则需要使用Pin<Box<T>>或者pin_utils::pin_mut!,例子如下:

    use pin_utils::pin_mut; // `pin_utils` is a handy crate available on crates.io
    
    // A function which takes a `Future` that implements `Unpin`.
    fn execute_unpin_future(x: impl Future<Output = ()> + Unpin) { /* ... */ }
    
    let fut = async { /* ... */ };
    execute_unpin_future(fut); // Error: `fut` does not implement `Unpin` trait
    
    // Pinning with `Box`:
    let fut = async { /* ... */ };
    let fut = Box::pin(fut);
    execute_unpin_future(fut); // OK
    
    // Pinning with `pin_mut!`:
    let fut = async { /* ... */ };
    pin_mut!(fut);
    execute_unpin_future(fut); // OK
    

    相关文章

      网友评论

        本文标题:005 Rust 异步编程,Pin 介绍

        本文链接:https://www.haomeiwen.com/subject/pxhnqktx.html