esp-rs / std-training

Embedded Rust on Espressif training material.

Home Page:https://esp-rs.github.io/std-training

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How create multiple asynchronous tasks,task have "loop{}"

xiaguangbo opened this issue · comments

There are multiple tasks like this that contain "loop{}", using "... after(...). await" prevents asynchronous tasks from blocking the scheduler entirely.
But using "block_on" will block the current thread, I want to create multiple asynchronous tasks in a thread, such as "tokio::task::spawn" to create an asynchronous task, this way of creating will not block the current thread, "esp-rs" is there anything similar

fn main() {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_svc::sys::link_patches();

    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    log::info!("Hello, world!");

    esp_idf_svc::hal::task::block_on(async {
        let mut timer_service = esp_idf_svc::timer::EspTaskTimerService::new()
            .unwrap()
            .timer_async()
            .unwrap();

        loop {
            log::info!("Hello, world!");
            timer_service
                .after(std::time::Duration::from_millis(1000))
                .await
                .unwrap();
        }
    });
}

rust asynchronous execution always follows a cooperative model. Meaning they never really run in true parallel fashion if they are bound to one thread. You probably first should read about how it works in general, one thing you can do is reading this

If you want to run fully independent async task that you dont want to handle the way described in the link, you can always just spawn a std::thread and create a separate block_on section for it.

While its strictly not needed you can use different async executors that does the job for you here, but it is essentially the same as calling block_on in a certain place. esp-idf-svc is executor agnostic so you can run executors like edge-executor, embassy-executor, smol, tokio just fine. But in the end you probably would also come to the same place where you would spawn different new executors on a different threads.

@Vollbrecht
Cargo.toml add tokio = { version = "*", features = ["rt"] }

tokio...block_on is ok.
tokio...spawn is no.
but cargo build no err

fn main() {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_svc::sys::link_patches();

    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    log::info!("Hello, world!");

    tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build().unwrap()
        .block_on(async {
            log::info!("Hello, world!");
            loop {
            }
        });

tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .unwrap()
            .spawn(async {
                log::info!("Hello, world!");

                let mut timer_service = esp_idf_svc::timer::EspTaskTimerService::new()
                    .unwrap()
                    .timer_async()
                    .unwrap();

                loop {
                    log::info!("Hello, world!");
                    timer_service
                        .after(std::time::Duration::from_millis(1000))
                        .await
                        .unwrap();
                }
            });
}

use tokio...spawn in the std::thread::spawn is no

std::thread::spawn(|| {
    tokio...spawn

    loop {
            std::thread::sleep(std::time::Duration::from_secs(1));
        }
});

At present, only block_on can be used. And you can't use tokio::time::sleep, say libc::... err

is ok

fn main() {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_svc::sys::link_patches();

    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    log::info!("Hello, world!");

    // esp_idf_svc::io::vfs::initialize_eventfd(5).unwrap();

    use tokio::time::{sleep, Duration};

    tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async move {
            tokio::spawn(async move {
                loop {
                    log::info!("tokio 2");
                    sleep(Duration::from_millis(1000)).await;
                }
            });

            loop {
                log::info!("tokio 1");
                sleep(Duration::from_millis(1000)).await;
            }
        });
}
[dependencies]
# ...
tokio = { version = "*", features = ["rt", "time"] }