Hey guys! I'm trying to grasp async/await usage with Tokio runtime.
Take a look at the following code: (Rust Playground)
// Cargo dependency:
// tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
async fn main() {
let handle = tokio::runtime::Handle::current();
assert_eq!(tokio::runtime::RuntimeFlavor::MultiThread, handle.runtime_flavor());
println!("Handle: {:?}", handle);
println!("Main: start {}", ct_id());
let task1 = handle.spawn(async {
println!("Task 1: start {}", ct_id());
let handle = tokio::runtime::Handle::current();
let task2 = handle.spawn(async {
println!("Task 2: start {}", ct_id());
std::thread::sleep(std::time::Duration::from_secs(3));
println!("Task 2: end {}", ct_id());
});
std::thread::sleep(std::time::Duration::from_secs(4));
println!("Task 1: sleeped {}", ct_id());
task2.await.unwrap();
println!("Task 1: end {}", ct_id());
});
std::thread::sleep(std::time::Duration::from_secs(5));
println!("Main: sleeped {}", ct_id());
task1.await.unwrap();
println!("Main: end {}", ct_id());
}
fn ct_id() -> String {
let t = std::thread::current();
format!("'{}' {:?}", t.name().unwrap_or_default(), t.id())
}
Note that std::thread::sleep
is there to represent some long-running blocking operation.
Given that we're running in multi-threaded runtime, I would expect something like this to be printed:
Main: start 'main' ThreadId(1)
Task 1: start 'tokio-runtime-worker' ThreadId(2)
Task 2: start 'tokio-runtime-worker' ThreadId(3)
Task 2: end 'tokio-runtime-worker' ThreadId(3)
Task 1: sleeped 'tokio-runtime-worker' ThreadId(2)
Task 1: end 'tokio-runtime-worker' ThreadId(2)
Main: sleeped 'main' ThreadId(1)
Main: end 'main' ThreadId(1)
Instead I'm seeing this:
Main: start 'main' ThreadId(1)
Task 1: start 'tokio-runtime-worker' ThreadId(2)
Task 1: sleeped 'tokio-runtime-worker' ThreadId(2)
Task 2: start 'tokio-runtime-worker' ThreadId(2)
Main: sleeped 'main' ThreadId(1)
Task 2: end 'tokio-runtime-worker' ThreadId(2)
Task 1: end 'tokio-runtime-worker' ThreadId(2)
Main: end 'main' ThreadId(1)
So while I was able to Handle::spawn
an asynchronously running task from main
, doing so from tokio-runtime-worker
does not have the effect I expected - instead, task is executed on the same worker thread despite handle being RuntimeFlavor::MultiThread
.
What am I missing here, and how do I really make use of multi-thread flavor? Thanks!