asynchronous - 为什么在等待点上持有非发送类型会导致非发送未来?
问题描述
在Send
trait的文档中,有一个很好的例子说明 Rc is not 之类的东西Send
,因为在两个不同的线程中克隆/删除会导致引用计数不同步。
然而,不太清楚的是,为什么在 an 中的一个点上持有与非Send
类型的绑定会导致生成的未来也是非类型。当编译器在异步手册的变通方法一章中过于保守时,我能够找到一种变通方法,但它并没有回答我在这里提出的问题。await
async fn
Send
也许有人可以举例说明为什么在 a 中有一个非Send
类型是Future
可以的,但在 anawait
中保持它不是?
解决方案
在异步函数中使用.await
时,编译器会在后台构建状态机。每个都.await
引入了一个新的状态(当它等待某事时),中间的代码是状态转换(也称为任务),它将基于一些外部事件(例如来自 IO 或计时器等)触发。
每个任务都被安排由异步运行时执行,它可以选择使用与前一个任务不同的线程。如果状态转换在线程之间发送是不安全的,那么结果Future
也不是Send
这样,如果您尝试在多线程运行时中执行它,则会出现编译错误。
Future
not to be完全没问题Send
,只是意味着你只能在单线程运行时执行它。
也许有人可以举例说明为什么在 a 中有一个非
Send
类型是Future
可以的,但在 anawait
中保持它不是?
考虑以下简单示例:
async fn add_votes(current: Rc<Cell<i32>>, post: Url) {
let new_votes = get_votes(&post).await;
*current += new_votes;
}
编译器将构造一个这样的状态机(简化):
enum AddVotes {
Initial {
current: Rc<Cell<i32>>,
post: Url,
},
WaitingForGetVotes {
current: Rc<Cell<i32>>,
fut: GetVotesFut,
},
}
impl AddVotes {
fn new(current: Rc<Cell<i32>>, post: Url) {
AddVotes::Initial { current, post }
}
fn poll(&mut self) -> Poll {
match self {
AddVotes::Initial(state) => {
let fut = get_votes(&state.post);
*self = AddVotes::WaitingForGetVotes {
current: state.current,
fut
}
Poll::Pending
}
AddVotes::WaitingForGetVotes(state) => {
if let Poll::Ready(votes) = state.fut.poll() {
*state.current += votes;
Poll::Ready(())
} else {
Poll::Pending
}
}
}
}
}
在多线程运行时,每次调用poll
都可能来自不同的线程,在这种情况下,运行时会在调用它之前将其移动到另一个线程。这不起作用,因为无法在线程之间发送。AddVotes
poll
Rc
但是,如果未来只是Rc
在同一个状态转换中使用了一个,那会很好,例如如果votes
只是一个i32
:
async fn add_votes(current: i32, post: Url) -> i32 {
let new_votes = get_votes(&post).await;
// use an Rc for some reason:
let rc = Rc::new(1);
println!("rc value: {:?}", rc);
current + new_votes
}
在这种情况下,状态机将如下所示:
enum AddVotes {
Initial {
current: i32,
post: Url,
},
WaitingForGetVotes {
current: i32,
fut: GetVotesFut,
},
}
没有在Rc
状态机中捕获,因为它是在状态转换(任务)中创建和删除的,所以整个状态机(又名Future
)仍然是Send
.
推荐阅读
- node.js - 向一个用户发送好友请求,如果他拒绝,则将其发送给其他用户,如果他也拒绝,则将其发送给其他用户,依此类推
- javascript - $.getJSON 不适用于 JavaScript 库
- html - 使用 CSS 悬停时无法在文本下方显示列表
- php - PHP,WordWrap如何只剪切文本而不剪切html代码
- python - 无法从 django 管理面板上传大文件
- python - 如何用pyside切换qml渲染器
- sql - 如何屏蔽 SQL SERVER 中的前几位和后几位?
- http - 主机名中超过 3 级的“子域”和“子域”是什么?
- css - 为什么在 Sencha Themer 中点击 Publich -> Apply to Theme App 后样式没有变化?
- java - 如何在 docker-compose 中为 Flink 设置 HADOOP_CLASSPATH