How to think about async/await in Rust
221–230 of 268 posts
Re: How to think about async/await in Rust
#222Earlier quoted context omitted.
One advantage of async/await is that its easier to cancel things. For example, this leads to the design pattern where you have multiple futures and you want to select the one that finishes first and cancel the rest. In regular threaded programming, cancellation is a bit more painful as you need to have some type of cancellation token used each time the thread waits for something. This a) is more verbose and b) can le…
>In regular threaded programming, cancellation is a bit more painful No, it isn't. Nothing is stopping your threading library from implementing the same thing. It just turns out it is a bad idea to kill threads at random points in time because they may own things like locks. Or in the case of async await doing something that is thought to be atomic.
In the end, its about expressing a state machine in a more concise implicit way, which is a suitable level of abstraction for most use cases.
Re: How to think about async/await in Rust
#223Earlier quoted context omitted.
Desktop operating systems all have application event loops that run within a single thread because the OS thread scheduler is not the same thing. If you just want an event loop, trying to use threads instead for everything will often end up in tears due to concurrent data access issues.
An async/await runtime doesn't necessarily need to run everything on the same thread though (that's really just a Javascript runtime restriction), instead it could use a thread pool. In this case, the same concurrent data issues would apply.
Re: How to think about async/await in Rust
#224Earlier quoted context omitted.
Maybe I’m wrong but the “asynchronous programming started with JavaScript” take doesn’t seem factual.
Correct, I believe it originated with Microsoft via .Net in the mid-2000s and was picked up by the JS ecosystem much later. The fact that Microsoft had a hand in async/await’s emergence may influence how people feel about it.
I can see this.
Re: How to think about async/await in Rust
#225Earlier quoted context omitted.
Web Workers are not parallel - they are only concurrent to the main context: think OS threads. Async JS is akin to using very lightweight simulated threads. You will not necessarily utilize more CPU cores by spawning additional Web Workers because they are not inherenent parallel. The actual performance of Web Workers depend on how your browser and OS schedules threads. They are OS threads despite the mountain of mis…
I thought the primary purpose of web workers was that the browser can run the workers in parallel to the main thread. As the spec says: > [Web workers] allow long tasks to be executed without yielding to keep the page responsive
You don't get extra calculation performance with web workers. You just create the illusion of a smooth experience because you don't block painting. It does not complete faster.
Re: How to think about async/await in Rust
#226Earlier quoted context omitted.
Desktop operating systems all have application event loops that run within a single thread because the OS thread scheduler is not the same thing. If you just want an event loop, trying to use threads instead for everything will often end up in tears due to concurrent data access issues.
Use one or more service threads to do most work off the UI thread.
In no way does it mean that you don’t need an event loop because threads exist, as was the contention here.
Re: How to think about async/await in Rust
#227Earlier quoted context omitted.
> Your comment seems to be conflating concurrency with parallelism. No, it really doesn't. I mention both concurrency and parallelism, and their main difference. > Threads are the opposite: They are interfaces for parallel programming No, they are not. Threads can do both. When waiting for an i/o bound operation, a thread can simply sleep. Added bonus: A thread basd implementation supports io bound concurrency and cp…
> When waiting for an i/o bound operation, a thread can simply sleep. I mean if you're fine with blocking I/O then obviously you don't need async, but on the other hand having non-blocking I/O is the whole point of async ^^
A language + runtime like Go or Erlang doesn't so much have "blocking" or "non-blocking" IO as the terms simply not applying. I see them yielding far more confusion than understanding when people try to come from Node and apply them to such threaded runtimes.
But if you had to force a term on such a system, the better understanding is that everything in a large-number-of-threads language+runtime is non-blocking. Both terms yield incorrect understanding, but that one gets you closer to the truth.
Re: How to think about async/await in Rust
#228Not specific to rust, but I think asynchronous programming in general is a hype. It didn't start because it is so awesome, it started because JS can't do parallel any other way. That's the long and short of it. People wanted to use JS in the backend for some reason. The backend requires concurrency. JS cannot do concurrency. Enter the event loop. Then enter some syntactic sugar for the event loop. And since JS is pop…
Sure, there are other ways to handle these things like polling state, but async makes it trivial and readable.
But this mostly applies to JS which has a kickass implementation of async/await. Whatever C++ tried to do, it's an awful mess so there I still use threads with busy loops, polling, etc., whatever makes sense for the task at hand.
Re: How to think about async/await in Rust
#229Not specific to rust, but I think asynchronous programming in general is a hype. It didn't start because it is so awesome, it started because JS can't do parallel any other way. That's the long and short of it. People wanted to use JS in the backend for some reason. The backend requires concurrency. JS cannot do concurrency. Enter the event loop. Then enter some syntactic sugar for the event loop. And since JS is pop…
Some believe that async makes the code easier to reason with, especially in cases where num_threads at least start with async/await if there is a paying customer involved. I think handling I/O becomes a joy when you have these abstractions at your disposal.
The other angle is performance. If you are reaching for async programming out of the gate because you want to go fast, you are making an epic mistake. Unnecessary context switching between threads will chop multiple orders of magnitude off a single thread best case. Unless you are 900% sure that the cost of communicating between threads is worth the squeeze, you should stick with a single-threaded paradigm (or use async/await responsibly).
Now, you may decide that losing performance in order to leverage more "sugary" programming primitives is worthwhile. We certainly make that decision many times over throughout - Interpreted languages, GC, etc. While I could implement our webapp using a socket select server and still easily meet our performance objectives, the complexity of managing this is not worth it. Async/await would still kick my ass in terms of performance because the runtime has been so carefully tuned around it. I can beat it in latency terms, but only for a trivial # of clients.
Re: How to think about async/await in Rust
#230Earlier quoted context omitted.
async/await allows to do concurrency without the need for explicit synchronization to shared data structures. E.g. I can do: loop { select! { _ = src_channel.readable() => src_channel.read(&mut buffer), _ = dst_channel.writable() => dst_channel.write(&mut buffer), } } without any mutex guarding the buffer, even though the reads and writes happen concurrently and share the same mutable buffer. This is possible because…
This is great until someone calls you through with a multi-threaded task runner and you've just gone and added consistency and race conditions to your code.