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