你把几个概念混为一谈了。
Concurrency is not parallelism
,和
async
和
await
是用来
,这有时可能意味着它们也是并行的工具。
此外,是否立即轮询future与所选语法是正交的。
异步
/
关键词
异步
和
使创建和交互异步代码更容易阅读,看起来更像“普通”同步代码。据我所知,在所有有这些关键字的语言中都是这样。
更简单的代码
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)
}
之后
async fn long_running_operation(a: u8, b: u8) -> u8 {
a + b
}
注意,“before”代码基本上是
implementation of today's
poll_fn
function
另请参见
Peter Hall's answer
关于如何更好地跟踪许多变量。
工具书类
可能令人惊讶的事情之一
异步
/
等待
它实现了一种以前不可能实现的特定模式:在未来使用引用。下面是一些以异步方式用值填充缓冲区的代码:
之前
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)
}
无法编译:
error[E0597]: `data` does not live long enough
--> src/main.rs:33:17
|
33 | fill_up_old(&mut data).map(|_| data)
| ^^^^^^^^^ borrowed value does not live long enough
34 | }
| - `data` dropped here while still borrowed
|
= note: borrowed value must be valid for the static lifetime...
error[E0505]: cannot move out of `data` because it is borrowed
--> src/main.rs:33:32
|
33 | fill_up_old(&mut data).map(|_| data)
| --------- ^^^ ---- move occurs due to use in closure
| | |
| | move out of `data` occurs here
| borrow of `data` occurs here
|
= note: borrowed value must be valid for the static lifetime...
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
}
呼叫
异步
函数不运行任何操作
系统的实现与设计
Future
异步
和
. 事实上,锈菌在灭绝之前有一个欣欣向荣的异步生态系统(比如东京)
/
等待
你为什么不呢
未来
在创建时是否立即进行了民意测验?
要获得最权威的答案,请查看
this comment from withoutboats
在RFC请求中:
语言是铁锈的未来不做任何事,除非民意调查。这个
整个系统都是围绕这一点构建的:例如,取消是
正是因为这个原因放弃了未来。相反,在其他方面
马上。
关于这一点的一点是,异步;在铁锈中等待不是天生的
并行构造。如果您的程序只使用异步;
以定义的、静态已知的、线性顺序执行。显然,大多数
事件循环上的并发任务,但它们不必这样做。这是什么
意思是你可以在本地保证
某些事件,即使其间执行了非阻塞IO
你希望与一些更大的非局部变量集异步
请求处理程序,同时与许多其他请求并发
处理程序,甚至在等待点的两侧)。
这个属性赋予Rust的async/await语法类似于本地的语法
推理和;低水平的控制,使生锈是什么。跑起来
到了第一个等待点就不会违反这一点-你会
仍然知道代码何时执行,它只会在两分钟内执行
不同的地方取决于它是在
等待。不过,我认为决定由其他语言开始
立即执行很大程度上源于他们的系统
(例如,这就是我对潜在问题的印象
来自Dart 2.0文档)。
this discussion from munificent
Erik Meijer,他也为C#开发了async/await。在C#中,异步/等待
C#的模型过于混乱,而是指定异步
当时,我和我团队中的另一个人的任务是
包管理器。基于这种经验,我们感觉到了异步函数
应该同步运行到第一个等待。我们的论点是
主要是:
-
做。即使在你能忍受的情况下,流血也是一种阻力
到处都是小表演。
-
总是让步意味着某些模式不能使用async/await实现。尤其是,像这样的代码非常常见
(此处为伪代码):
getThingFromNetwork():
if (downloadAlreadyInProgress):
return cachedFuture
cachedFuture = startDownload()
return cachedFuture
以前创建的挂起的未来。你要确保你不会
操作多次。这意味着你需要同步
如果异步函数从一开始就是异步的,那么上面的函数就不能使用async/await。
我们为自己辩护,但最终语言设计师还是坚持了下来
结果是打错电话了。性能成本是真实的
以至于许多用户形成了“异步函数是
慢”并开始避免使用它,即使是在性能命中的情况下
价格实惠。更糟糕的是,我们看到了令人讨厌的并发错误
认为他们可以在函数的顶部做一些同步工作
当发现他们创造了比赛条件时感到沮丧。总的来说
执行任何代码。
所以,对于飞镖2,我们现在要做的是非常痛苦的改变
将异步函数更改为与第一个wait和
在第一天。
但根据我们的经验,同步到第一个等待显然是更好的
折衷飞镖。
cramert replies
(请注意,有些语法现在已经过时了):
功能如下:
fn foo() -> impl Future<Item=Thing> {
println!("prints immediately");
async_block! {
println!("prints when the future is first polled");
await!(bar());
await!(baz())
}
}
这些示例使用Rust1.39和FuturesCrate0.3.1中的异步支持。
C代码的文字转录
use futures; // 0.3.1
async 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 = long_running_operation(1, 2);
another_operation(3, 4);
sum.await
}
fn main() {
let task = foo();
futures::executor::block_on(async {
let v = task.await;
println!("Result: {}", v);
});
}
如果你打电话来
foo
-
实施的东西
Future<Output = u8>
已返回。
福
并推动它完成(通过轮询,在本例中是通过
futures::executor::block_on
-
实施的东西
从呼叫返回
long_running_operation
(尚未开始工作)。
-
another_operation
因为它是同步的。
-
这个
.await
语法导致代码
开始。这个
福
在计算完成之前,future将继续返回“not ready”。
输出为:
foo
another_operation
long_running_operation
Result: 3
注意这里没有线程池:这都是在单个线程上完成的。
异步
阻碍
阻碍:
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
}
异步
阻塞,然后等待两个操作完成,此函数才能完成。
请注意,像这样包装同步代码
对于任何需要很长时间的事情都是个好主意;看见
What is the best approach to encapsulate blocking I/O in future-rs?
更多信息。
使用线程池
// 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
}