Based on my understanding of worker threads, there would be multiple tasks/jobs are handled at the same time when multiple workers are configured, but the below code snippet shows that those jobs/tasks are handle in sequence, it seems that there is no difference when worker_threads is configured 1 or 4:
use tokio::runtime;
use tokio::time::{sleep, Duration};
use chrono::prelude::*;
fn main() {
let runtime = runtime::Builder::new_multi_thread()
.worker_threads(4)
.enable_time()
.on_thread_start(|| {
println!("thread worker started");
})
.build().unwrap();
runtime.block_on(async {
let f = (0..100).map(|n|{
tokio::spawn(async move {
let utc: DateTime<Utc> = Utc::now();
println!("time: {} value: {}", utc, n);
sleep(Duration::from_secs(2)).await;
})
});
for f in f {
f.await;
}
});
}
output below, check the timestamp of every single line:
thread worker started
thread worker started
thread worker started
thread worker started
time: 2023-01-28 11:06:26.221026 UTC value: 0
time: 2023-01-28 11:06:28.224528 UTC value: 1
time: 2023-01-28 11:06:30.227851 UTC value: 2
time: 2023-01-28 11:06:32.232228 UTC value: 3
time: 2023-01-28 11:06:34.234764 UTC value: 4
time: 2023-01-28 11:06:36.239956 UTC value: 5
time: 2023-01-28 11:06:38.245066 UTC value: 6
time: 2023-01-28 11:06:40.248575 UTC value: 7
time: 2023-01-28 11:06:42.253783 UTC value: 8
time: 2023-01-28 11:06:44.259469 UTC value: 9
time: 2023-01-28 11:06:46.260561 UTC value: 10
time: 2023-01-28 11:06:48.266170 UTC value: 11
The entire point behind async/await is that you can run lots of tasks on a single thread. Even if you use the current-thread runtime that uses only a single thread, you should still see all of them running at the same time.
@alice thanks, is there any criteria how many async tasks can be scheduled on a single thread? I mean if I don't know the criteria I can not set the worker_threads correctly either.
When it comes to choosing how many worker threads to use, the important metric is how much work your async tasks are doing. You can have four tasks that do a lot of work and overload your runtime, or you can have a million tasks that are all idle and consume no resources other than memory. In the former case, you might want several worker threads to spread out your four expensive tasks on different threads. In the latter case, a single worker thread might be enough.