TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Async Rust: What is a runtime? how Tokio works under the hood

178 点作者 sylvain_kerkour将近 3 年前

8 条评论

armchairhacker将近 3 年前
Basically, async Rust requires you to understand how “async” works under the hood. Javascript async does stuff automatically, there is an implicit runtime and you can just await or .then and get a Promise and closure which encapsulates whatever data you need and stores it on the heap.<p>Rust lets you optimize the runtime and do polling etc. how you want. But you have to do everything explicitly. And you can store futures on the stack and customize how their represented (including the closures), but you have problems like async traits because every future has a different type.<p>Lots of people say “async Rust is hard because async is hard”. Honestly this is false, async is easy if you Box dyn everything and use the tokio runtime. Async Rust is hard - and Rust in general is hard - because they don’t compromise performance for abstraction and encapsulation. You get exposed to the gritty internals of how async works in exchange for being able to make futures and a runtime which are optimal for your specific program.
评论 #32120579 未加载
评论 #32119846 未加载
评论 #32120955 未加载
评论 #32120189 未加载
评论 #32121192 未加载
评论 #32133340 未加载
pkulak将近 3 年前
People crap on async Rust because it&#x27;s not the most graceful to use, but I think it&#x27;s kinda genius how they&#x27;ve managed to make it zero-overhead. To the point that even the stack size of green &quot;threads&quot; is known ahead of time.<p>The only issue I have is that it&#x27;s tough not to use it. The big HTTP libraries let you opt out, but smaller libraries don&#x27;t have the resources to do everything twice. I don&#x27;t know what the solution is, but it would be nice to always be able to chose. It&#x27;s pretty silly to use async networking in a cli app, for example, but sometimes you have to.
评论 #32119957 未加载
评论 #32119521 未加载
评论 #32119398 未加载
ThePhysicist将近 3 年前
I recently got started with Rust and was surprised that different async runtimes were not compatible with each other. In principle Rust has a similar interface concept like Golang, so it should be possible to specify desired behavior of a component and leave implementation to the library, so that you can switch between different ones without worrying about compatibility. Pretty much a Rust noob still so maybe I&#x27;m missing something that makes this difficult&#x2F;impossible though.<p>I&#x27;ve been thinking about rewriting a network library using async, but the whole async ecosystem seems a bit fragmented and immature: mio would probably be everything I need but it doesn&#x27;t support channels (there&#x27;s mio-extras which does but it&#x27;s not compatible with the latest mio version). Tokio would probably fit the bill, though it seems to be too complex for what I actually need (just a way to poll sockets and channels to see if there&#x27;s anything to read from them).
评论 #32121951 未加载
评论 #32121478 未加载
samsquire将近 3 年前
The behind the details of Rust async is rather hard to follow There&#x27;s waiters and polling that only execute your function to progress and there&#x27;s pending and done. Any help to understand the relationship between the waiters, pollers and executor and runtime things would be appreciated.<p>I wrote a M:N thread scheduler in C, Java and Rust. The C version also can schedule file reading to an IO thread but I&#x27;m nowhere near finished.<p><a href="https:&#x2F;&#x2F;github.com&#x2F;samsquire&#x2F;preemptible-thread" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;samsquire&#x2F;preemptible-thread</a><p>Another of my ideas is to rewrite synchronous code into parallel LMAX disruptors. In other words a tree of RingBuffer each line of synchronous code its own event loop. Rather than one event loop multiplexing events from different systems you pipeline every blocking call. I think it would be very fast.<p>Here&#x27;s a write-up.<p><a href="https:&#x2F;&#x2F;github.com&#x2F;samsquire&#x2F;ideas4#51-rewrite-synchronous-code-into-lmax-disruptor-thread-pools---event-loops-that-dont-block-on-cpu-usage" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;samsquire&#x2F;ideas4#51-rewrite-synchronous-c...</a>
redman25将近 3 年前
Is it possible to build a tokio compatible library that might not be so heavy weight? Maybe this a moot point since libraries would use tokio as a dependency anyway.
评论 #32120556 未加载
评论 #32122946 未加载
saurik将近 3 年前
So, as someone who has been working heavily with coroutines and continuations for decades in a number of different languages across the gamut of programming paradigms, I don&#x27;t really understand why these runtimes aren&#x27;t &quot;interoperable&quot;, and am hoping I just have a different idea of what that word means than the people who talk about them in the context of Rust.<p>Like, right now I maintain a large almost-entirely-asynchronous C++ codebase using their new C++20 co_await monstrosity, and while I find the abstraction ridiculously wide and a bit obtuse, I have never had trouble &quot;interoperating&quot; different &quot;runtimes&quot; and I am not even sure how one could screw it up in a way to break that... unless maybe these &quot;executors&quot; are some attempt to build some kind of pseudo-thread, but I guess I just feel like that&#x27;s so &quot;amateur hour&quot; that I would hope Rust didn&#x27;t do that (right?).<p>So, let&#x27;s say you are executing inside of a coroutine (context is unspecified as it doesn&#x27;t matter). When this coroutine ends it will transfer control to a continuation it was given. It now wants to block on a socket, maybe managed by Runtime A (say, Boost ASIO). That involves giving a continuation of this coroutine past the point of the transfer of control to Runtime A which will be executed by Runtime A.<p>Now, after Runtime A calls me--maybe on some background I&#x2F;O thread--I decide I would prefer y task to be executing in Runtime B. I do this sometimes because I might have a bit of computation to do but I don&#x27;t want to block an I&#x2F;O thread so I would prefer to be executing inside of a thread pool designed for slow background execution.<p>In this case, I simply await Runtime B (which in this case happens to be my lightweight queue scheduler). I don&#x27;t use any special syntax for this because all of these runtimes fully interoperate: I used await to wait for the socket operation and now I use await to wait until I can be scheduled. The way these control transfers work is also identical: I pass a continuation of myself after the point of the await to the scheduler which will call it when I can be scheduled.<p>Now remember, at the beginning of this I was noting that something unspecified had called me. That is ostensibly a Runtime C here (maybe I was waiting for a callback from libwebrtc--which maintains its own runloop--because I asked it to update some ICE parameter, which it does asynchronously). It doesn&#x27;t matter what it was, because now that &quot;already happened&quot;: that event occurred and the continuation I provided was already executed and has long since completed <i>and returned</i> as I went on immediately to pass a continuation to someone else rather than blocking.<p>Is this somehow not how Rust works? Is await some kind of magic &quot;sticky&quot; mechanism that requires the rest of this execution happen in the context of the &quot;same&quot; runtime which is executing the current task? I have seen people try to do that--I am looking at you, Facebook Folly--but, in my experience, attempts to do that are painfully slow as they require extra state and cause the moral equivalent of a heavyweight context switch for every call as you drag in a scheduler in places where you didn&#x27;t need a scheduler.<p>But, even when people do that, I have still never had an issue making them interoperate with other runtimes, so that can&#x27;t be the issue at its core. I guess I should stare at the key place where the wording in this article just feels weird?... to me, I&#x2F;O and computation are fairly disjoint, and so I can&#x27;t imagine why you would ever want to have your I&#x2F;O scheduler do &quot;double-duty&quot; to also handle &quot;task queues&quot;. When I&#x2F;O completes it completes: that doesn&#x27;t involve a &quot;queue&quot;. If you want to be part of a queue, you can await a queue slot. But it sounds like tokio is doing both? Why?
评论 #32119851 未加载
评论 #32120995 未加载
评论 #32119877 未加载
评论 #32120368 未加载
aaaaaaaaaaab将近 3 年前
So, in other languages, async introduces two function &quot;colors&quot;: sync and async. But in Rust you also have unique &quot;colors&quot; for each async runtime?<p>I must be misunderstanding something, because this sounds like pretty hare-brained...
评论 #32119842 未加载
评论 #32121573 未加载
评论 #32120113 未加载
评论 #32119967 未加载
评论 #32119961 未加载
swayvil将近 3 年前
Ever notice that &quot;rust&quot; is a contraction of &quot;red dust&quot;?<p>(Also, words are usually first coined as onomatopoeias. For example &quot;dust&quot; sounds like a swisshing pile of dust. But &quot;red&quot;. That probably has more stuff going on there. Added layers)