Why does a tokio thread wait for a blocking thread before continuing?

Why does my blocking thread output twice before the other thread outputs once? I'd like to be able to spawn off tasks to run in the background (immediately) but not wait for them to complete before the main code continues. Is there an easy way to do this with tokio?

Code:

use std::{thread, time};
use chrono;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Starting up");
    tokio::spawn(blocking_thread()).await.unwrap();
    Ok(())
}

async fn blocking_thread() {
    for i in 1..5 {
        println!("{} Blocking thread {}", chrono::offset::Utc::now(), i);
        tokio::spawn(other_thread(i));
        thread::sleep(time::Duration::from_millis(100)); //In my real code this is a blocking syscall
    }
}

async fn other_thread(i: u8) {
    println!("{} Other thread {}", chrono::offset::Utc::now(), i);
}

Output:

Starting up
2022-01-21 09:03:36.662248332 UTC Blocking thread 1
2022-01-21 09:03:36.762375086 UTC Blocking thread 2
2022-01-21 09:03:36.762617994 UTC Other thread 1
2022-01-21 09:03:36.862634161 UTC Blocking thread 3
2022-01-21 09:03:36.862913141 UTC Other thread 2
2022-01-21 09:03:36.963055279 UTC Blocking thread 4
2022-01-21 09:03:36.963383710 UTC Other thread 3
2022-01-21 09:03:37.063496911 UTC Other thread 4

Why does my blocking thread output twice before the other thread outputs once

Use tokio::time::sleep for sleeping in the blocking_thread. Because thread::sleep will not yield execution to the main tokio executer.

I'd like to be able to spawn off tasks to run in the background (immediately) but not wait for them to complete before the main code continues.

You are waiting on tokio::spawn(blocking_thread()).await.unwrap(); line for the thread to complete. Don't await if you want to continue in the main.