003Rust 非同步程式設計,Future trait 介紹

linghuyichong發表於2020-06-08

Future介紹

Future是Rust非同步程式設計的核心,Rust非同步程式設計基本都是圍繞Future來展開。那麼,什麼是Future呢?
首先,我們來看下簡化版的Future,如下:

trait SimpleFuture {
    type Output;
    fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
}

enum Poll<T> {
    Ready(T),
    Pending,
}
  • executor
    Future的執行者,Future是具有的惰性的,並不會自己的執行,所以需要有一個執行者(executor)來執行Future。

  • Poll列舉型別

表示Future的兩個狀態:Ready為完成狀態,Pengding為未完成狀態。

  • poll函式

executor透過呼叫poll函式可以推進Future的狀態。呼叫poll時,如果完成,返回Ready狀態;如果未完成則返回pengding狀態。當wake函式被呼叫後,會通知executor再次呼叫poll函式。

  • wake函式

當Future變成Ready狀態後,wake函式會被呼叫,executor就知道哪個Future已經準備好了,然後呼叫poll函式。

使用SimpleFuture

use std::thread;
use std::time::Duration;

enum Poll<T> {
    Ready(T),
    Pending,
}

trait SimpleFuture {
    type Output;
    //fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
    fn poll(&mut self, wake: u32) -> Poll<Self::Output>;
}

static mut FINISHED: bool = false;

struct MySleeper {
    polls: u64,
    wake: u32,
}

impl MySleeper {
    fn new() -> Self {
        MySleeper {
            polls: 0,
            wake: 0,
        }
    }
}

impl SimpleFuture for MySleeper {
    type Output = ();
    fn poll(&mut self, wake: u32) -> Poll<()> {
        unsafe {
        if FINISHED {
            Poll::Ready(())
        } else {
            self.wake = wake;
            self.polls += 1;
            println!("not ready yet --> {}", self.polls);
            Poll::Pending
        }
        }
    }
}
struct MyReactor {
    wake: u32,
    handle: Option<thread::JoinHandle<()>>,
}

impl MyReactor {
    fn new() -> MyReactor {
        MyReactor {
            wake: 0,
            handle: None,
        }
    }

    fn add_wake(&mut self, wake: u32) {
        self.wake = wake;
    }

    fn check_status(&mut self) {
        if self.handle.is_none() {
            let _wake = self.wake;
            let handle = thread::spawn(|| loop {
                thread::sleep(Duration::from_secs(5));
                {//模擬執行wake函式
                    unsafe {
                        FINISHED = true;
                    }
                }
            });

            self.handle = Some(handle);
        }
    }
}

struct MyExecutor;

impl MyExecutor {
    fn block_on<F: SimpleFuture>(mut my_future: F, wake: u32) {
        loop {
            match my_future.poll(wake) {
                Poll::Ready(_) => { 
                    println!("my future execute ok!");
                    break; 
                },
                Poll::Pending => {
                    unsafe {
                        while !FINISHED {//FINISHED為true表示為喚醒
                            thread::sleep(Duration::from_secs(1));
                        }
                    }
                }
            }
        }
    }
}


fn main() {
    let mut reactor = MyReactor::new();
    let sleeper = MySleeper::new();
    let wake = sleeper.wake;
    reactor.add_wake(wake);
    reactor.check_status();
    MyExecutor::block_on(sleeper, wake);
}
  • SimpleFuture

簡化版的Future,實際上是一個狀態機。

  • MyExecutor

用來執行SimpleFuture的相關動作。

  • MyReactor

用來檢測條件是否就位,從而通知MyExecutor執行SimpleFuture。

Future trait

真正的Future的定義如下:

trait Future {
    type Output;
    fn poll(
        // Note the change from `&mut self` to `Pin<&mut Self>`:
        self: Pin<&mut Self>,
        // and the change from `wake: fn()` to `cx: &mut Context<'_>`:
        cx: &mut Context<'_>,
    ) -> Poll<Self::Output>;
}

真正的Future中,poll函式中self的型別變為了Pin<&mut Self>,第二個引數wake: fn()變為 &mut Context<'_>

第一個改變是因為,透過Pin可以建立不可移動的 Future。不可移動的物件可以在它們的欄位之間儲存指標,例如:

struct MyFut { a: i32, ptr_to_a: *const i32 }

第二個改變是因為,喚醒Future的時候,需要帶一些資料,我們之前的只是wake函式無法滿足需求。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
令狐一衝

相關文章