Rustのtokioクレートのruntimeモジュールのメモ。
tokio::runtimeを使うには、rt-multi-threadフィーチャーを指定する必要がある。
[dependencies] tokio = { version = "1.42.0", features=[〜, "rt-multi-thread"] }
非同期処理(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の中で(#[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()でスレッドを起動することが出来るが、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`