S-JIS[2024-12-15] 変更履歴

tokio::runtimeメモ

Rusttokioクレートのruntimeモジュールのメモ。


概要

tokio::runtimeを使うには、rt-multi-threadフィーチャーを指定する必要がある。

Cargo.toml:

[dependencies]
tokio = { version = "1.42.0", features=[〜, "rt-multi-thread"] }

Runtime

block_on

非同期処理(asyncが付いている関数)を同期処理(asyncの付いていない関数)から呼びたい場合に、Runtimeのblock_onを使う。

ただし、同一スレッド内でネストしてblock_onを使うことは出来ない。
つまり、同一スレッド内では、block_onのブロックの中で、もう一度block_onを呼び出すことは出来ない。

そして、実行する関数に#[tokio::main]#[tokio::test]が付けられていると、実際にはblock_onが使われている。
(#[tokio::main]や#[tokio::test]はマクロであり、block_onを使ったプログラムに展開される)

したがって、自分のプログラムで単純にblock_onを使うことは出来ないが、
別スレッドを起動すればblock_onを使うことが出来る。

#[tokio::main]
async fn main() {
    std::thread::scope(|scope| {
        scope.spawn(move || {
            let runtime = tokio::runtime::Runtime::new().unwrap();
            runtime.block_on(async {
                println!("sleep start");
                tokio::time::sleep(Duration::from_secs(2)).await;
                println!("sleep end")
            });
        });
    });
}

scope.spawn()で別スレッドを起動している。
scope()はブロック内の処理が終了するまで待つらしく、コンパイラーがライフタイムを認識できるからエラーにならないようだ。


同一スレッドで二重にblock_onを呼ぶ例(実行時エラーになる例)

同一スレッド内でblock_onの中で(#[tokio::main]を使った関数の中で)単純にblock_onを呼ぶと、実行時エラーになる。

#[tokio::main]
async fn main() {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(async {
        println!("sleep start");
        tokio::time::sleep(Duration::from_secs(2)).await;
        println!("sleep end")
    });
}

↓実行例

thread 'main' panicked at 〜\tokio-1.42.0\src\runtime\scheduler\multi_thread\mod.rs:86:9:
Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks. 
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\rust-example1.exe` (exit code: 101) 

std::thread::spawn()の例(コンパイルエラーになる例)

std::thread::spawn()でスレッドを起動することが出来るが、block_onの中にselfを含めたい場合、ライフタイムの問題でコンパイルエラーになる。

struct Example {
    id: i32
}

impl Example {
    fn blocking(&self) {
        let t = std::thread::spawn(move || {
            let runtime = tokio::runtime::Runtime::new().unwrap();
            runtime.block_on(async {
                println!("sleep start {}", self.id);
                tokio::time::sleep(Duration::from_secs(2)).await;
                println!("sleep end")
            });
        });
        t.join().unwrap();
    }
}

error[E0521]: borrowed data escapes outside of method
  --> src/main.rs:44:17
   |
43 |       fn blocking(&self) {
   |                   -----
   |                   |
   |                   `self` is a reference that is only valid in the method body
   |                   let's call the lifetime of this reference `'1`
44 |           let t = std::thread::spawn(move || {
   |  _________________^
45 | |             let runtime = tokio::runtime::Runtime::new().unwrap();
46 | |             runtime.block_on(async {
47 | |                 println!("sleep start {}", self.id);
...  |
50 | |             });
51 | |         });
   | |          ^
   | |          |
   | |__________`self` escapes the method body here
   |            argument requires that `'1` must outlive `'static`

tokioへ戻る / Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま