From 2368d7adcad9e5a5f40371e7cfb2206a6578c701 Mon Sep 17 00:00:00 2001 From: Nazmul Idris Date: Sat, 10 Feb 2024 23:18:42 -0600 Subject: [PATCH] Update rust-tokio tutorial w/ info on how tokio tasks are implemented --- _posts/2022-03-12-rust-tokio.md | 46 ++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/_posts/2022-03-12-rust-tokio.md b/_posts/2022-03-12-rust-tokio.md index d9ba5de2..0d2fb2ab 100644 --- a/_posts/2022-03-12-rust-tokio.md +++ b/_posts/2022-03-12-rust-tokio.md @@ -16,12 +16,12 @@ categories: - - + - [Introduction](#introduction) - [Concurrency and async/await, vs parallelism](#concurrency-and-asyncawait-vs-parallelism) - [Async/await, Rust, and Tokio](#asyncawait-rust-and-tokio) + - [Learn more about Tokio](#learn-more-about-tokio) - [Implementing async middleware w/ function pointers](#implementing-async-middleware-w-function-pointers) - [Implementing async middleware w/ async traits](#implementing-async-middleware-w-async-traits) - [Writing tests](#writing-tests) @@ -31,9 +31,10 @@ categories: - [With macros](#with-macros) - [Wrapping up](#wrapping-up) - + ## Introduction + This article illustrates how to write concurrent and parallel code in Rust using Tokio. The pedagogical example we will use is building an asynchronous implementation of a middleware runner @@ -52,6 +53,7 @@ more complex TUI apps next using crates like `termion` and `tui`. {%- include featured.html -%} ## Concurrency and async/await, vs parallelism + Concurrency is being able to break up your program or function into smaller tasks that can be interleaved, possibly on the _same thread_. This approach lends itself well to speeding up _many_ IO @@ -120,6 +122,7 @@ especially useful in tasks that are CPU bound (and not IO bound). > Rust. ## Async/await, Rust, and Tokio + You don't need to use [Tokio](https://https://tokio.rs/) in order to use `async` and `await` in Rust. However, Tokio is very powerful and makes very easy to do complex things with it. @@ -153,17 +156,29 @@ fn main() { } ``` -> 💡 Learn more about Tokio [here](https://tokio.rs/tokio/tutorial). -> -> - It provides a great introduction for what use cases Tokio is good for and what use cases that it -> doesn't really work for. -> - For example if you're reading a lot of files, then you can just use an ordinary thread pool in -> Rust instead of Tokio, since it doesn't really provide additional benefit over it. -> - Another example is if your tasks involve running lots of CPU bound computations in parallel then -> you should consider using [`rayon`](https://docs.rs/rayon/latest/rayon/). -> - However if you are doing a lot of IO bound tasks at the same time then Tokio rocks 🎉. +### Learn more about Tokio + + +Basics: +- https://tokio.rs/tokio/tutorial +- It provides a great introduction for what use cases Tokio is good for and what use cases + that it doesn't really work for. +- For example if you're reading a lot of files, then you can just use an ordinary thread + pool in Rust instead of Tokio, since it doesn't really provide additional benefit over + it. +- Another example is if your tasks involve running lots of CPU bound computations in + parallel then you should consider using [`rayon`](https://docs.rs/rayon/latest/rayon/). +- However if you are doing a lot of IO bound tasks at the same time then Tokio rocks 🎉. + +Deep dives: +1. You can get more info on this topic + [here](https://users.rust-lang.org/t/socket-per-thread-in-tokio/83712/7). +2. For an even deeper dive into how Tokio tasks themselves are implemented for intra-task + concurrency, please take a look at this [excellent + article](https://without.boats/blog/let-futures-be-futures/). ## Implementing async middleware w/ function pointers + A Redux middleware is just a function. It takes an action as an argument, and may return nothing, or it may return a new action. The middleware is where you are allowed to run side effects. So it is a @@ -420,6 +435,7 @@ Some(Result(3)) ``` ## Implementing async middleware w/ async traits + You can use the `async-trait` crate in order to use the `async` keyword in your trait methods. This is an alternative approach to using function pointers in the previous section. @@ -438,6 +454,7 @@ and also can be made `async`). > you like it 🙏. ## Writing tests + Tokio provides [testing support](https://docs.rs/tokio/latest/tokio/attr.test.html) for the code that we've just written. Here's an integration test for the middleware functions that are shown @@ -484,6 +501,7 @@ async fn test_complex_mw_example_works() { ``` ## Advanced topic - locks and tokio + The standard library provides `RwLock` and `Mutex` types. These are meant to be used using "regular" blocking code, rather than "async" code. You can spawn threads that work well with these locks. @@ -557,12 +575,14 @@ pub fn spawn( ``` ## Async lambdas + > 🪄 Currently `async` lambdas are only supported in Rust nightly channel, after enabling the > feature `async_closure`. Please see this > [async RFC for more details](https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md#async--closures). ### Without macros + The following snippet is an example of an `async` function that accepts a lambda, w/out enabling `async_closure`. Note that the return type is of type `Fun` which is `Future`. @@ -591,6 +611,7 @@ where ``` ### With macros + The `SafeListManager` struct shown below simply wraps a `Vec` in an `async` `RwLock` in an `Arc` and manages that reference, allowing for a safe way to add and remove items from the list. And passing @@ -686,6 +707,7 @@ for subscriber_fn in list.iter() { > [here](https://developerlife.com/2022/03/12/rust-redux/). ## Wrapping up + This is a simple introduction to Tokio. The tutorials and videos are a great resource for learning Tokio, along w/ the tutorials that are provided on the [Tokio website](https://tokio.rs/).