• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

rust异步编程Async背后原理

武飞扬头像
上后左爱
帮助1

async 背后原理

  • move 关键,能够让async 闭包和块使用,能够获取它引用变量所有权,使用父的变量,如下所示:
use futures::executor;
async fn move_block() {
    let my_string = "foo".to_string();
    let f = async move {
        println!("{}", my_string);
    };
    // println!("{}", my_string); //此处不能再使用my_string
    f.await
}
fn main() {
    executor::block_on(move_block());
}
  • async 背后关键字:
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;
    // futures::join(f, async_function3());
}
学新通

对其展开分析
对于Future而言有 Pending,Ready状态

 async 将 代码块转换成Future某个状态
 struct AsyncFuture {
		 fut_one: FutFunction1,
		 fut_two: FutFunction2,
		 state: State		 
}
enum State {
    AwaitFut1,
    AwaitFut2,
    Done,
}
-- Future的trait 的内部原理演示
impl Future for AsyncFuture {
   type Output = ();

   fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
       loop {
           match self.state {
               State::AwaitFut1 => match self.fut_one.poll(...) {
                   Poll::Ready(()) => self.state = State::AwaitFut2,
                   Poll::Pending => return Poll::Pending,
               }
               State::AwaitFut2 => match self.fut_two.poll(...) {
                   Poll::Ready(()) => self.state = State::Done,
                   Poll::Pending => return Poll::Pending,
               }
               State::Done => return Poll::Ready(());
           }
       }
   }
}
学新通
  • 创建一个匿名的结构体
  • 为结构体定义对应的状态
  • 实现Future trait

Pin 内容

PIN使用场景从源头来说:async/await 的实现机制,从上面例子而言,对于闭包,编译器创建一个隐式的内部匿名struct 保存捕获到变量,对struct实现Call方法来实现函数的调用,由于需要记录当前状态(每一次await 时候都会导致一个状态),所以生成一个匿名enum , 每个enum变体保存从外部或者之前await捕获变量, enum充当函数的虚拟栈,主要因为如果保存在stack中,但是由于await 存在导致局部变量会从stack 中被删除(提出需要将匿名struct分配到堆上)-- 最大问题是rust对于自引用不能完美解决

async fn async_main() {
    // 异步块
    let f = async {
        let mut x = [0: 128];
        let async_put = async_put_data_to_buf(&mut x);
        async_put.await;
    };
}
--
自引用结构体
struct Foo {
	array:[Bar; 10],
	ptr: &'array Bar, // 内部ptr 指向的是其他Filed的元素,如果在Foo实例变量进行移动(memcpy),移动后ptr依然指向之前的地址,导致空指针异常,这就使用PIN 弱化版本智能指针解决问题
}
-- 这些变量需要分配在堆上

PIN 解决的问题:PIN类型包着指针类型, 保证指针类型背后值不被移动
PIN使用场景和抽象API了解点击
也就是说使用PIN指针 解决 自引用的变量被意外的移动后但是还是指向移动前地址,导致的空指针异常错误, PIN是一个弱化版本的BOx智能指针, 防止泄露&mut T 能够防止对象呗移动 Pin就像是一个铁笼子, 将自引用的猛兽关进去后,依然可以正常观察它,或者给它投点食物修改它,也可以把铁笼子移来移去,但不能把它放出来自由活动。

  • PIN是零开销:
    使用async构建异步逻辑时候并不需要每处都进行内存分配,将异步逻辑构建成一整个task放入到executor后最后在同一进行内存分配,这就是PIN零开销,将Future分配在堆上。
  • PIN指针引入,使得rust代码在不使用unsafe 前提下,支持编译器生成的自引用结构,async 函数可以从虚拟栈中借用数据
-- 在没有使用PIN之前Future API: Vec<u8> 数据传递
pl Socket {
    fn read(self, buf: Vec<u8>) ->
        impl Future<Item = (Self, Vec<u8>, usize), Error = (Self, Vec<u8>, io::Error)>;
}
// 使用PIN之后
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> impl Future<Item = usize, Error = io::Error >   'a

-- 对应async函数 直接使用 ,不需要管自引用结构导致空指针异常
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error>;

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgkjjab
系列文章
更多 icon
同类精品
更多 icon
继续加载