5 ms·
> Yes the RwLock and mpsc comes from Tokio and lets you .await instead of blocking a thread, but these are not async primitives, these are multi-threading synch
by resonious 3y ago
> Yes the RwLock and mpsc comes from Tokio and lets you .await instead of blocking a thread, but these are not async primitives, these are multi-threading synchronization primitives.
The only reason all this async stuff even exists is because we want concurrency. We want to say "while this one task waits for I/O, this other task will do stuff". So it's not too surprising to me that an intro to async would include synchronization primitives. Those primitives aren't really "thread"-specific if by thread you mean OS thread. When you do async like this, you're basically re-implementing OS threads in user space.
- hinkley 3y agoAsync/await is green threads with better scoping and syntactic sugar. Which lets be honest, is something green threads desperately needed.
- duped 3y agoThey're not green threads. Futures are stackless coroutines.
- Ygg2 3y agoAnd green threads aren't a panacea. They introduce runtimes, which was something Rust avoided on purpose.
- spinningslate 3y agoTrue. Rust's mandate for "zero runtime cost abstractions" is one of the better justifications for async/await. Because async/await, too, has significant limitations, notably: 1. Coloured functions 2. Placing the concurrency design decision (async vs not) on the implementer of a function, not the caller 3. Debugging complexity (1) and (2) are mutually, negatively, reinforcing. As the implementer of a library function, I can't possibly know all the possible scenarios in which my function will be called. If I make it synchronous, then I'm blocking my caller. Which might be an issue - or might not. If I make it async then I don't block the caller - but I've just consigned the entire call stack to be async. There's clearly strong opinions on both sides. Some are strong advocates of async/await. Personally, I'm not. Multiple, concurrent, fine-grained sequences of actions fit my mental model much better. Async/await doesn't give me that: I find reading async/await code to be a bit like reading control flow with GOTOs. My preferences are undoubtedly influenced by working with Erlang, which has supported fine-grained sequences since the beginning. There are no coloured functions; just functions. As the caller, I can decide if I want things to run sequentialy (call from another function) or concurrently (spawn as a separate Erlang process). I have the context I need to make that decision. It's not a panacea by any means (see e.g. Structured Concurrency [0]). But it fits my mental model much better. YMMV of course. -- [0]: https://en.wikipedia.org/wiki/Structured_concurrency https://en.wikipedia.org/wiki/Structured_concurrency
- Ygg2 3y agoI'm not fan of async, but I don't like to deny reality by saying just use Structured Concurrency/Green threads/Magic Multithreading Powder. They all have their trade-offs. And to be fair they are working to solve Colored functions. However, async is still half-baked because no support for async traits and other corner cases. Which was again caused by async being in RFC, development hell.
- pjmlp 3y agoAnd then everyone has to bundle tokio anyway.
- Ygg2 3y agoTokio might is not a panacea either. If they pulled it in std, backwards compatibility would kill most evolution.
- pjmlp 3y agoAt a certain point, one needs to question evolution at any price.
- Ygg2 3y agoSure, but I honestly I think std should have things you DON'T want to evolve. Stuff like time/chrono. And YAML. Pretty sure Rust developers want to move std into crates as well, as well as parts of compiler. And I completely understand them.
- foldr 3y agoI think the article's point is that if you're doing single-threaded concurrency then you don't (or at least might not) need synchronization primitives. If you're writing a Node script, for example, then you don't have to worry about two callbacks trying to modify the same variable at the same time because you know that there is only one thread of execution.
- rklaehn 3y agoMulti threading and concurrency are not the same. You can have a very high performance server that handles thousands of requests concurrently on a single thread. That's how node/deno do things. But the way to do things in async rust is that if you want concurrency you also have to use multithreading. At least that is what you see in all the examples and docs. As soon as you require your futures to be Send you have to use not just concurrency but full blown multithreading primitives. So e.g. you have to use Arc<RwLock<...>> where a Rc<RefCell<...>> would suffice. Now, to be fair, both futures and tokio support local futures. There is boxed_local (https://docs.rs/futures/latest/futures/future/trait.FutureExt.html#method.boxed_local https://docs.rs/futures/latest/futures/future/trait.FutureEx...) and tokio-util even comes with an optional local task pool (https://docs.rs/tokio-util/latest/tokio_util/task/struct.LocalPoolHandle.html https://docs.rs/tokio-util/latest/tokio_util/task/struct.Loc...). But these features are hidden and certainly not promoted in examples and documentation.
- menaerus 3y ago> So e.g. you have to use Arc<RwLock<...>> In terms of C++ code that would equate to std::shared_ptr<std::shared_mutex<...>> which ... sounds quite wasteful in terms of scalability/performance. Why is it not possible to simply return a Rust promise? That's the way I do it in my C++ async (executor) library backed by work-stealing queues under the hood.
- masklinn 3y ago> Why is it not possible to simply return a Rust promise? It… is? An Arc<RwLock> is what you need to share mutable data between “promises”.
- deleted 3y ago[deleted]
- menaerus 3y agoI understand that part. What I don't understand is why https://docs.rs/promises/latest/promises/struct.Promise.html https://docs.rs/promises/latest/promises/struct.Promise.html are not used instead.
- deleted 3y ago[deleted]
- mgaunard 3y agoWaiting for I/O does not require using multiple threads. You can have the same thread do other things until I/O is ready.