Live data from Hacker News

How to think about async/await in Rust

cliffle.com

221–230 of 268 posts

Re: How to think about async/await in Rust

#222
post #11

Earlier 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.

Well each time you use the await keyword you are saying its a safe point to exit, which is more predictable than killing at random points. Holding locks across await points is an anti-pattern, and Rust at least can give a hint if you try to do that. Async/await implementations will also generally allow you to run cleanup code on cancellation (but the exact mechanism depends on the language).

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

#223
post #165

Earlier 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.

Yes, basically golang model

Re: How to think about async/await in Rust

#224
post #195

Earlier 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.

> 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

#225
post #194

Earlier 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

The workers don't block painting and they do not run in a separate process. That's why it's concurrent but not parallel. The web worker does work whenever the main thread is not painting and there is a free time slot. The browser is not painting all the time.

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

#226
post #165

Earlier 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.

Yes, sure. Operating systems nowadays provide useful thread pool runtimes for this purpose, like Apple’s GCD.

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

#227

Earlier 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 ^^

I've seen a lot of people who seem to think all blocking IO completely blocks the entire OS process.

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

#228

Not 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…

I strongly disagree. Async/Await is one of the nicest, cleanest ways to deal with asynchronous tasks, and asynchronous tasks are everywhere. Loading data from disk without blocking and doing something once this is done -> async. Memcpying data from CPU to GPU without blocking -> async. Memcpying data from GPU back to CPU without blocking -> async.

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

#229

Not 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…

I feel like there are 2 mostly independent angles to this:

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

#230

Earlier 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.

But this approach already works with multithreaded runner and there are no data races or consistency problems. They would be caught by rust borrow checker anyways.
Post reply on HN