PIN和痛苦 - 异步防锈教程

2021-03-30 01:19:29

我想认为我对&#34的理解;异步生锈"过去一年左右增加了。我' m 100%船上的基本原理:我会觉得使用少数线程处理数千个并发任务。听起来不错!

并变得精通异步生锈,我接受了很多东西。蓝色功能和重新碰交,红色(异步)功能具有传染性。

EH,并非完全 - 您可以在蓝色(同步)函数toblock中构建执行者,直到完成红色(异步)函数。

我猜困难是当你从红色到蓝色时,回到红色 - 因为你可以在executer内制作一个执行者。

看熊,这就是我谈论的是什么:在那里'我这么多,我必须得到批准。 " Don' t阻止了执行者&#34 ;,"未来任何人都无所作为,除非轮询","您需要在轮询它之前固定未来"

但熊,一切都是如此简单的同步生锈,突然间我忍受了所有这些规则?我必须考虑这些?

在这里让我告诉你。说,在同步生锈,我只想打印你好,睡觉,然后打印再见。

什么?我没有看到任何堵塞。实际上,它表现得完全是同步版本。对我来说似乎没关系。

使用std :: {thread ::睡眠,时间::持续时间}; #[tokio :: main] async fn main(){让一个= tokio :: spawn(hold());让两个= tokio :: spawn(hold());让(_,_)= tokio ::加入! (一,两个);} async fn hell(){println! ("你好!");睡眠(持续时间:: from_millis(500)); println! ("再见!");}

那里!它只是有效!谢谢熊,我不知道我抱怨的是什么,异步生锈真的很容易。

什么?哦,哦,等一下,我看到了它,我可以刚刚将味道论证传递给tokio :: main属性宏:

//👇#[tokio :: main(风味=" current_thread")] async fn main(){let一个= tokio :: spawn(hold());让两个= tokio :: spawn(hold());让(_,_)= tokio ::加入! (一,两);} //省略:其他一切

所以我们阻止了执行者。但是,看,'他们的复杂性,我不得不思考一切!

对我来说,睡觉只是一个syscall.你打电话给它和繁荣!它睡觉了。无需担心任何事情。

使用STD ::时间::持续时间;使用tokio ::时间::睡眠; #[tokio :: main(风味=" current_thread")] async fn main(){let一个= tokio :: spawn(hold());让两个= tokio :: spawn(hold());让(_,_)= tokio ::加入! (一,两个);} async fn hell(){println! ("你好!");睡眠(持续时间:: from_millis(500)).await; println! ("再见!");}

但是,看到熊,我不知道这一切的作品。它' s都只是伏都教给我.One函数阻止了执行者,另一个并不是为什么?在引擎盖下它真的是如何建立的?

虽然Tokio的睡眠返回了未来......这将在第一次轮询它时注册一个计时器......只达到截止日期时它只完成。

我呃......没有任何意义。我不记得"轮询"任何事物。和你说它..."寄存器"计时器?如同,全球州?你的意思是什么"完成&#34 ;?

是的,为什么不,如果它可以在整个事情上脱掉一些光线,我会弄清楚未来。

所以我想自'是一个特征......我知道特质。我们可以在any型式中实施它?像空的结构?

好的,编译器抱怨,并非所有特征项目都已实施:itwants产出和民意调查。现在怎么办?

MyFuture {型输出; FN民意调查(SEC:STD :: PIN :: PIN<& ul self>,cx:& mut std :: task :: task :: task :: task<>> std ::任务::民意调查< self ::输出> { 去做 ! ()}}

使用STD :: {未来::未来,PIN :: PIN,任务:: {上下文,轮询},}; struct myfuture {} ill ich将来{型输出; FN民意调查(Self:Pin<< um self>,cx:& mut语调< 39; _>) - >民意调查< self ::输出> { 去做 ! ()}}

好吧,首先,您需要选择您的输出类型。你想要你的未来,嗯,产出吗?

ich将来为myfuture {type output =(); FN民意调查(Self:Pin<< um self>,cx:& mut语调< 39; _>) - >民意调查< self ::输出> { 去做 ! ()}}

$货物运行--quietthread' main'恐慌AT'尚未实施',SRC / MAIN。 RS:19:9:使用“rust_backtrace = 1`环境变量来显示回溯运行

$ rust_backtrace = 1个货运run -quietthread'主要' and#39;尚未实施,尚未实施。 :恐慌:: panic_fmt在/rustc/cb75ad5db02783e8b0222fee363c5f63f7e2cf5b/library/core/src/panicking.rs:92:14 2:核心::恐慌::惊慌/rustc/cb75ad5db02783e8b0222fee363c5f63f7e2cf5b/library/core/src/panicking.rs:50 :5 3:< manual_futures :: myfuture作为核心::未来::未来:: future> ::民意调查at ./src/main.rs:19:9 4:manual_futures :: main :: {{closure}}在./src/main.rs:10:5 5:<核心::未来:: from_generator :: genfuture< t>作为核心::未来::未来::未来和gt; ::民意调查/home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/未来/ mod.rs:80:19 6:<核心:: pin :: pin< p>作为核心::未来::未来::未来和gt; ::民意调查/home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/未来/未来.rs:119:9 7:tokio :: runtime :: basic_scheduler ::内部< p> :: block_on :: {{closure}} :: {{closure}}在/home/amos/.cargo/注册表/ src / github.com-1ECC6299DB9EC823 / TOKIO-1.4.0 / SRC / RUNTETE / BASIC_SCHEDULER.RS:196:62 8:TOKIO :: COOP :: _BUDGET :: {{closure}}在/ home / amos /。 CARGO / REGINGRY / SRC / GITHUB.COM-1ECC6299DB9EC823 / TOKIO-1.4.0 / SRC / COOP.RS:106:9 9:STD :: Thread :: local :: localKey< t> :: try_with在/ home / amos /.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:272:16 10:std :: thread :: local :: localkey< t> ::在/home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustib/src/rust/library/std/src/thread/local.rs:248: 9 11:tokio :: coop :: with_budget在/home/amos/.cargo/registry/src/github.com-1cc6299db9ec823/tokio-1.4.0/src/coop.rs:99:5 12:tokio :: coop :: /家庭预算/amos/.cargo/registry/src/github.com-1c6299db9ec823/tokio-1.4.0/src/coop.rs:76:5 13:tokio :: runtime :: basic_scheduler ::内部< p> :: block_on: :{{closure}}在/home/amos/.cargo/registry/src/github.com-1c6299db9ec823/tokio-1.4.0/src/runtime/basic_scheduler.rs:196:39 14:tokio ::运行时:: basic_scheduler ::输入:: {{closure}}在/home/amos/.cargo/registry/src/github.com-1cc6299db9ec823/tokio-1.4.0/src/runtime/basic_scheduler.rs:279:29 15:tokio ::宏:: scoped_tls :: scopedkey< t> ::坐在/home/amos/.cargo/registry/src/github.com-1c6299db9ec823/tokio-1.4.0/src/macros/scoped_tls.rs:61: 9 16:tokio ::运行时:: basic_scheduler ::在/home/amos/.cargo/registry/src/github.com-1c6299db9ec823/tc/.4.0/src/runtime/basic_scheduler.rs:279:5 17 :tokio :: runtime :: basic_scheduler ::内部< p> :: block_on在/home/amos/.cargo/registry/src/github.com-1c6299db9ec823/tokio-1.4.0/src/runtime/basic_scheduler.rs: 185:9 18:tokio :: runtime :: basic_scheduler :: connguard< p> :: block_on在/ home / Amos / .cargo / registry / src / github.com-1ECC6299DB9EC823 / TOKIO-1.4.0 / SRC / RUNTETE / BASIC_SCHEDULER.RS:425:9 19:TOKIO :: Runtime :: Basic_scheduler :: Basicscheduler< p> p> :: block_on在/home/amos/.cargo/registry/src/github.com-1cc6299db9ec823/tokio-1.4.0/src/runtime/basic_scheduler.rs:145:24 20:tokio :: runtime ::运行时:: block_on at /主页/ amos / .cargo / registry / src / github.com-1ECC6299DB9EC823 / TOKIO-1.4.0 / SRC / RUNTIME / MOD.RS:450:46 21:MANUAL_FUTURES :: MAIN AT ./src/main.rs:7 :1 22:核心:: ops ::函数:: fnonce :: call_once在/home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/ src / ops / function.rs:227:5?省略了一些细节,使用`rust_backtrace = full`进行详细回溯运行。

fn main(){let rt = tokio ::运行时:: builder :: new_current_thread()。建造 ( ) 。 unwrap();让FUT = myfuture {}; RT。 block_on(fut);}

好的,好的,好吧,我真的想建立一个运行时自己,我' m feedwith tokio :: main版本现在。

好吧,回来!你可以看到它&#39应该返回一个轮询,这是一个有两个变体的枚举。

..我假设我们在未来完成时返回,好吧。所以,如果我这样做:

使用STD :: {未来::未来,PIN :: PIN,任务:: {上下文,轮询},}; #[tokio :: main] async fn main(){让fut = myfuture {}; println! ("等待Fut ..."); FUT .aiawait; println! ("等待Fut ......完成!");} struct myfuture {} iclilcure for myfuture {type output =(); FN民意调查(Self:Pin<<<< um>,_cx:& mut语调< 39; _>) - >民意调查< self ::输出> {//👇轮询::就绪(())}}

正确的!而另一个变体是待处理的,如果yourfuture尚未完整,您应该返回。

ich将来为myfuture {type output =(); FN民意调查(Self:Pin<<<< um>,_cx:& mut语调< 39; _>) - >民意调查< self ::输出> {//👇轮询:: pending}}

不好了。它没有被阻止'嗯,实际的,生成的同步主功能被封锁在异步主要和#39;未来 - 它' s等待完成,它从未完成。

所以它'没有再次被调查?但是你刚刚告诉我..挂断,让Metjust添加一些调试打印:

ich将来为myfuture {type output =(); FN民意调查(Self:Pin<<<< um>,_cx:& mut语调< 39; _>) - >民意调查< self ::输出> {//👇println! (" myfuture :: poll()");民意调查:: pending}}

是的!它'唯一的被调查一次!什么给予,熊?我以为你告诉我会再次受到调查吗?

啊,它只会再次被调查再次被调查。 Didyou认为它会在循环中调查吗?想象一下,如果你尝试读取套接字,另一个对等体没有发送五秒钟。

阅读未来将被轮询和调查并在繁忙的循环中轮询并轮询了Fiveseconds。它会消耗整个CPU核心!不,期货只是"唤醒"什么时候发生了什么。

有趣的事情!喜欢,一个计时器耗尽。或者已准备好的文件。那种东西。

啊。那种东西。好的,所以说我希望我的未来成为"唤醒"在第二个。我该怎么做?

啊,右 - 轮询方法采用两个参数。接收器是......某种形式的自我,所以,myfuture,第二个论点是一个& mut背景。

让' s看它有什么......它有...返回A&amp的Waker()方法;歌剧!看起来很有趣。

和威克有...唤醒和唤醒_by_ref方法。好吧,我们可以' t叫醒唤醒,因为它想要掌握威克,我们所有的所有权都是一个不可改变的参考。

ich将来为myfuture {type output =(); FN民意调查(Self:Pin<< um self>,cx:& mut语调< 39; _>) - >民意调查< self ::输出> {println! (" myfuture :: poll()"); CX。 Waker()。 key_by_ref();民意调查:: pending}}

$ Cargo run -quietawaiting fut ... myfuture :: poll()myfuture :: poll()myfuture :: poll()myfuture :: poll()myfuture :: poll()myfuture :: poll()myfuture :: poll()轮询()myfutur ^ c(那行很快重复,只有在我们点击Ctrl-c时停止)

heyyyy。现在它是一个忙碌的循环!它尽可能快地被调查。

但那'仍然不是我想要的。我想要的是......为了我的未来在一秒钟后再次遭到攻击。

所以???只需使用帖子!你只是继续介绍如何轻松的线程,而且你明白他们,这位布拉拉布拉。只需使用一个线程。

但我只参考歌诈,我可以'哦,你会看到它,它可以实现克隆。好的。好的。我猜我' ll刚刚产卵。

ich将来为myfuture {type output =(); FN民意调查(Self:Pin<< um self>,cx:& mut语调< 39; _>) - >民意调查< self ::输出> {println! (" myfuture :: poll()");让Waker = CX。 Waker()。克隆(); std :: thread :: spawn(move || {std :: thread :: sleep(持续时间:: from_secs(1));摇摆。唤醒();});民意调查:: pending}}

啊啊!有趣的。好吧,最终我猜我想回来准备好,所以一切都是保持一些状态,让' s看...

#[tokio :: main] async fn main(){让fut = myfuture :: new(); println! ("等待Fut ..."); FUT .aiawait; println! ("等待Fut ......完成!"); struct myfuture {slept:bool,} iclich myfuture {fn new() - > self {self {slept:false}}} illicure for myfuture {type output =(); FN民意调查(Self:Pin<< um self>,cx:& mut语调< 39; _>) - >民意调查< self ::输出> {println! (" myfuture :: poll()");匹配自我。睡觉{false => {//确保我们在一秒钟内再次轮询,让Waker = CX。 Waker()。克隆(); std :: thread :: spawn(move || {std :: thread :: sleep(持续时间:: from_secs(1));摇摆。唤醒();});自己 。睡觉=真;民意调查::待处理} true => POLL ::就绪(()),}}}}

不,等待 - 它抱怨自己是不可变形的。呃......我可以写它,如果那个' d verv?

ich将来为myfuture {type output =(); //👇FN轮询(MUT SELF:PIN< um self>,cx:& mum; mul nut nully<>) - >) - >民意调查< self ::输出> { // ETC。 }}

它真的是' t。嘿等一分钟。我们'重新返回使用线程,有点。

我不想每次想要等待时旋转一个新的线程。似乎过度过度。肯定的Async rust™有一些东西可以解决这个问题吗?

是的,是的,那个我们使用的东西。但我们可以在Myfuture内嵌入它,豆腐仍然可以看到机器。

啊啊!所以,如果我只是将其存储为myfuture的领域,我必须做的是ispoll它,从我自己的民意调查方法中?

使用tokio ::时间::睡眠; struct myfuture {睡眠:睡眠,} iclich myfuture {fn new() - > self {self {leep:tokio ::时间::睡眠(持续时间:: from_secs(1)),}}}} islicure for myfuture {type输出=(); FN民意调查(MUT SELF:PIN<< um self>,cx:& mum; mul nut语言< _>) - >民意调查< self ::输出> {println! (" myfuture :: poll()");自己 。睡觉 。民意调查(CX)}}

啊,太棒了!它甚至具有与MyFuture ::民意调查民意调查结果相同的精确返回类型

$货物检查检查手册 - 期货V0.1.0(/ home / amos / ftl /手动期货)错误[e0599]:没有命名为struct`睡眠状态的poll`的方法 - > SRC / MAIN.RS:35:20 | 35 | self.sleep.poll(cx)| ^^^^在`sleep`error中找不到的方法:由于此错误的更多信息,尝试使用前一个错误,请尝试使用“rustc - explain e0599`.Error:无法编译”手册期货“以了解更多信息,运行命令再次与 - 鼠标。

哦!我知道!民意调查是特征未来的方法,所以我需要将其导入能够调用它...等待不,我们已经导入了它。

看看接收者!它' s不是自我,它' s not' self,它' s不是甚至& mut self。

m好的。我们如何构建PIN ...它有一种新方法。让'试试。

ich将来为myfuture {type output =(); FN民意调查(MUT SELF:PIN<< um self>,cx:& mum; mul nut语言< _>) - >民意调查< self ::输出> {println! (" myfuture :: poll()"); //👇让mut sleep = pin :: new(& mut self。睡觉);睡觉 。民意调查(CX)}}

$ Cargo Check检查手册 - 期货V0.1.0(/ home / amos / ftl /手动期货)错误[e0277]:`phantompinned`不能卸载 - > src / main.rs:35:25 | 35 |让mut睡眠= Pin :: new(& mut self.sleep); | ^^^^^^^ ^^在`tokio ::时间:: drive ::睡眠:: _ :: __ orize' _>`,特质`unpin`没有为`phantompinned`而实施=注意:所需的是因为它出现在“tokio ::时间:: driver :: strider :: stry :: timerentry` =注意:所需的:它出现在类型”tokio :: time :: drive :: sleep :: _:_:_: :__原点<' _>`=注意:所需的:由于“睡眠”\ notel的icric icric的要求:PIN ::< p> :: new`

呃,好的......返回销<框<睡眠>>所以我猜我也必须改变菲尔德类型......

struct myfuture {睡眠:引脚<盒子<睡觉> >,} islich myfuture {fn new() - > self {self {sleep:box :: pin(tokio ::时间::睡眠(休眠时间:: from_secs(1))),}} iscrame for myfuture {type output =(); FN民意调查(MUT SELF:PIN<< um self>,cx:& mum; mul nut语言< _>) - >民意调查< self ::输出> {println! (" myfuture :: poll()");让睡眠=针:: new(& mut self。睡觉);睡觉 。民意调查(CX)}}

是的!而且你诚实地不要自己建造那个针,因为引脚<盒子>>已经固定 - 所以你'重新建立销<>>>>>>>>>>>>>>>>>>>>>>>>。

ich将来为myfuture {type output =(); FN民意调查(MUT SELF:PIN<< um self>,cx:& mum; mul nut语言< _>) - >民意调查< self ::输出> {println! (" myfuture :: poll()");自己 。睡觉 。 AS_MUT()。民意调查(CX)}}

您甚至可以进一步逐步并添加期货箱,以便您可以使用Futureext :: Poll_Unpin,因为轮询了取消投票的未来是一个公平的操作。

使用期货:: futureext; ich将来为myfuture {type output =(); FN民意调查(MUT SELF:PIN<< um self>,cx:& mum; mul nut语言< _>) - >民意调查< self ::输出> {println! (" myfuture :: poll()");自己 。睡觉 。 poll_unpin(cx)}}}

在这一点,我可以' t帮助但是奇迹 - 我们真的需要tounderstand吗?喜欢,它'我们整洁,我们曾经做过,不要给我错了。

但在现实世界中,有一点在手动实施未来? Don' t我们有异步/ await关键字,以便我们不要' t haveTo?

好吧!在这种情况下,是的,它的无偿。但是,我们说我们想用不同的东西......

让' s表示我们想要制作一种实现Tokio' sasyncreadInterface的类型(这样我们可以在任何地方使用另一个读者的地方),但是在每个读取之间人为地引入一些延迟。

你' D Than Thel这样的,但没有。不是现在。看,异步锈病仍处于其批评的少年。几乎一切都在那里,但它可以有点achale居住。

嗯,asyncread是一种特质。和特质可以' t具有异步方法。 (在这个写作的TheTime,即。用生锈1.51)。

对,是的,但是!我们始终读成某事。缓冲区。当我们这样做时,异步土地:

......如果我们&#39,我们早期返回;还没有准备好。我们只是回到轮询::等待,等待我们' requateder唤醒了。 Ohhhhh Andjust,因为我们返回并不意味着我们与缓冲区完成了!

所以它不是如何诅咒工作的。它确实给你一个缓冲区,但是你'重新调整到任一个:

准备好,然后你填充部分(或全部)缓冲区,并回到轮询::准备好

Gotcha。 好的,所以,我们知道如何通过调查睡眠未来来睡觉,我认为我们足够了解我们一个缓慢的读者...... 它将成为一个结构(它'总是一个结构),它和#39; ll与Anyki一起工作 ......