Asynchronous 在Rust中异步/等待的目的是什么?

Asynchronous 在Rust中异步/等待的目的是什么?,asynchronous,syntax,async-await,rust,future,Asynchronous,Syntax,Async Await,Rust,Future,在C#之类的语言中,给出以下代码(我不是故意使用wait关键字): 异步任务Foo() { var task=LongRunningOperationAsync(); //其他一些不相关的操作 另一个操作(); 结果=任务。结果; } 在第一行中,长操作在另一个线程中运行,并返回一个任务(这是一个未来)。然后,您可以执行与第一个操作并行运行的另一个操作,最后,您可以等待该操作完成。我认为这也是Python、JavaScript等语言中async/wait的行为 另一方面,在《铁锈》一书中,我读

在C#之类的语言中,给出以下代码(我不是故意使用
wait
关键字):

异步任务Foo() { var task=LongRunningOperationAsync(); //其他一些不相关的操作 另一个操作(); 结果=任务。结果; } 在第一行中,长操作在另一个线程中运行,并返回一个
任务
(这是一个未来)。然后,您可以执行与第一个操作并行运行的另一个操作,最后,您可以等待该操作完成。我认为这也是Python、JavaScript等语言中
async
/
wait
的行为

另一方面,在《铁锈》一书中,我读到:

Rust's futures和其他语言的futures之间的一个根本区别是,除非进行民意调查,否则Rust's futures不会做任何事情。整个系统都是围绕着这一点构建的:例如,取消正是因为这个原因而放弃了未来。相反,在其他语言中,调用异步fn会带来一个立即开始执行的未来


在这种情况下,Rust中的
async
/
wait
的目的是什么?与其他语言相比,这种表示法是运行并行操作的一种方便方法,但是如果调用
异步
函数不运行任何东西,我看不出它在Rust中是如何工作的。

您混淆了一些概念

async
await
是并发工具,这有时可能意味着它们也是并行工具

此外,是否立即轮询未来与所选择的语法是正交的

异步
/
等待
关键字
async
await
的存在使异步代码的创建和交互更易于阅读,并且看起来更像“正常”同步代码。就我所知,在所有有这些关键字的语言中都是如此

简单代码 这是创建未来的代码,在轮询时添加两个数字

之前

fn long_running_operation(a: u8, b: u8) -> impl Future<Output = u8> {
    struct Value(u8, u8);

    impl Future for Value {
        type Output = u8;

        fn poll(self: Pin<&mut Self>, _ctx: &mut Context) -> Poll<Self::Output> {
            Poll::Ready(self.0 + self.1)
        }
    }

    Value(a, b)
}
use std::io;

fn fill_up<'a>(buf: &'a mut [u8]) -> impl Future<Output = io::Result<usize>> + 'a {
    futures::future::lazy(move |_| {
        for b in buf.iter_mut() { *b = 42 }
        Ok(buf.len())
    })
}

fn foo() -> impl Future<Output = Vec<u8>> {
    let mut data = vec![0; 8];
    fill_up(&mut data).map(|_| data)
}
请注意,“before”代码基本上是

另请参阅如何更好地跟踪许多变量

工具书类
async
/
await
可能令人惊讶的一点是,它启用了一种以前不可能实现的特定模式:在未来使用引用。下面是一些以异步方式用值填充缓冲区的代码:

之前

fn long_running_operation(a: u8, b: u8) -> impl Future<Output = u8> {
    struct Value(u8, u8);

    impl Future for Value {
        type Output = u8;

        fn poll(self: Pin<&mut Self>, _ctx: &mut Context) -> Poll<Self::Output> {
            Poll::Ready(self.0 + self.1)
        }
    }

    Value(a, b)
}
use std::io;

fn fill_up<'a>(buf: &'a mut [u8]) -> impl Future<Output = io::Result<usize>> + 'a {
    futures::future::lazy(move |_| {
        for b in buf.iter_mut() { *b = 42 }
        Ok(buf.len())
    })
}

fn foo() -> impl Future<Output = Vec<u8>> {
    let mut data = vec![0; 8];
    fill_up(&mut data).map(|_| data)
}
之后

use std::io;

async fn fill_up(buf: &mut [u8]) -> io::Result<usize> {
    for b in buf.iter_mut() { *b = 42 }
    Ok(buf.len())
}

async fn foo() -> Vec<u8> {
    let mut data = vec![0; 8];
    fill_up(&mut data).await.expect("IO failed");
    data
}
换句话说,您有一个异步操作,可以在它完成之前调用多次。以后的通话也使用相同的方法 以前创建的等待未来。你要确保你不会开始 手术进行了多次。这意味着您需要同步 在开始操作之前检查缓存

如果异步函数从一开始就是异步的,则上述函数不能使用async/await

我们为自己辩护,但最终语言设计师还是坚持了下来 从顶部异步。这是几年前的事了

结果是打错了电话。性能成本是真实的 足够多的用户形成了“异步函数是 慢”,并开始避免使用它,即使在性能命中的情况下 这是可以负担得起的。更糟糕的是,我们看到人们在 认为他们可以在函数顶部做一些同步工作,然后 他们沮丧地发现自己创造了比赛条件。总的来看, 似乎用户以前不会自然而然地假设异步函数 执行任何代码

因此,对于Dart 2,我们现在要对其进行非常痛苦的更改 将异步函数更改为与第一个wait和 通过该转换迁移所有现有代码。我很高兴 我们正在做出改变,但我真的希望我们做了正确的事情 第一天

我不知道Rust的所有权和性能模型是否有所不同 对您的限制是,从顶部异步确实更好, 但根据我们的经验,同步到第一个等待显然是更好的 省道的权衡

(请注意,有些语法现在已经过时):

如果需要在调用函数时立即执行代码 你可以写下你的未来,而不是等到未来被调查的时候 功能如下:

fn foo() -> impl Future<Item=Thing> {
    println!("prints immediately");
    async_block! {
        println!("prints when the future is first polled");
        await!(bar());
        await!(baz())
    }
}
如果调用
foo
,Rust中的事件顺序将是:

  • 返回实现未来的内容
  • 就这样。“实际”工作尚未完成。如果您获取
    foo
    的结果并将其推向完成(在本例中通过
    futures::executor::block_on
    轮询),则接下来的步骤是:

  • 调用
    long\u running\u操作
    返回实现
    Future
    的内容(它尚未开始工作)

  • 另一个_操作
    是同步的,因此可以工作

  • .await
    语法导致
    long\u running\u操作中的代码启动。
    foo
    future将继续返回“notready”,直到计算完成

  • 产出将是:

    foo
    另一项行动
    长时间运行
    结果:3
    
    请注意,这里没有线程池:这都是在单个线程上完成的

    async
    块 您还可以使用
    async
    块:

    use futures::{future, FutureExt}; // 0.3.1
    
    fn long_running_operation(a: u8, b: u8) -> u8 {
        println!("long_running_operation");
    
        a + b
    }
    
    fn another_operation(c: u8, d: u8) -> u8 {
        println!("another_operation");
    
        c * d
    }
    
    async fn foo() -> u8 {
        println!("foo");
    
        let sum = async { long_running_operation(1, 2) };
        let oth = async { another_operation(3, 4) };
    
        let both = future::join(sum, oth).map(|(sum, _)| sum);
    
        both.await
    }
    
    在这里,我们将同步代码包装在一个
    async
    块中,然后等待这两个操作完成,然后该函数才能完成

    请注意,像这样包装同步代码对于任何实际需要很长时间的事情都不是一个好主意;有关更多信息,请参阅

    有一个线程池
    你混淆了几个概念

    async
    await
    是用于并发的工具,这有时可能意味着它们也是用于并发的工具
    use futures::{future, FutureExt}; // 0.3.1
    
    fn long_running_operation(a: u8, b: u8) -> u8 {
        println!("long_running_operation");
    
        a + b
    }
    
    fn another_operation(c: u8, d: u8) -> u8 {
        println!("another_operation");
    
        c * d
    }
    
    async fn foo() -> u8 {
        println!("foo");
    
        let sum = async { long_running_operation(1, 2) };
        let oth = async { another_operation(3, 4) };
    
        let both = future::join(sum, oth).map(|(sum, _)| sum);
    
        both.await
    }
    
    // Requires the `thread-pool` feature to be enabled 
    use futures::{executor::ThreadPool, future, task::SpawnExt, FutureExt};
    
    async fn foo(pool: &mut ThreadPool) -> u8 {
        println!("foo");
    
        let sum = pool
            .spawn_with_handle(async { long_running_operation(1, 2) })
            .unwrap();
        let oth = pool
            .spawn_with_handle(async { another_operation(3, 4) })
            .unwrap();
    
        let both = future::join(sum, oth).map(|(sum, _)| sum);
    
        both.await
    }
    
    getData(url)
       .then(response -> parseObjects(response.data))
       .then(data -> findAll(data, 'foo'))
       .then(foos -> getWikipediaPagesFor(foos))
       .then(sumPages)
       .then(sum -> console.log("sum is: ", sum));
    
    async {
        let response = await getData(url);
        let objects = parseObjects(response.data);
        let foos = findAll(objects, 'foo');
        let pages = await getWikipediaPagesFor(foos);
        let sum = sumPages(pages);
        console.log("sum is: ", sum);
    }
    
    async {
        let response = await getData(url);
        let objects = parseObjects(response.data);
        let foos = findAll(objects, 'foo');
        let pages = await getWikipediaPagesFor(foos);
        let sum = sumPages(pages, objects.length);
        console.log("sum is: ", sum, " and status was: ", response.status);
    }
    
    getData(url)
       .then(response -> Promise.resolve(parseObjects(response.data))
           .then(objects -> Promise.resolve(findAll(objects, 'foo'))
               .then(foos -> getWikipediaPagesFor(foos))
               .then(pages -> sumPages(pages, objects.length)))
           .then(sum -> console.log("sum is: ", sum, " and status was: ", response.status)));