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`