Rustのtokio::mainのthreadどうなってるの?
Tokio mainってどうなってるのか。まずドキュメントを読む
#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
The worker_threads option configures the number of worker threads, and defaults to the number of cpus on the system. This is the default flavor.
Note: The multi-threaded runtime requires the rt-multi-thread feature flag.
なるほど。multi_threadがデフォルトなのか。
tokio::mainが実際どういうコードを出力しているか確認していく。
cargo new tokio-main-test
cargo add tokio --features="macros, rt, rt-multi-thread"
で、cargo-expandもいれとく
cargo add cargo-expand
cargo-expandはnightlyじゃないと動作しないのでrustupで上書きしとく
rustup override set nightly
で、mainを書いてみる。
#[tokio::main]
async fn main() {
println!("Hello, world!");
}
このままだと待たないので、3秒待つ関数を追加する。
#[tokio::main]
async fn main() {
println!("start");
wait().await;
println!("finish");
}
async fn wait() {
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
}
この状態でcargo runとかで実行すると、startから3秒まってfinishが出力される。
これをcargo expand
で展開すると下記のコードになる。
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
let body = async {
{
::std::io::_print(format_args!("start\n"));
};
wait().await;
{
::std::io::_print(format_args!("finish\n"));
};
};
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
{
return tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(body);
}
}
async fn wait() {
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
}
bodyは単純にmainの中身を包んだものになる。
ドキュメントにある通り、tokioのランタイムはデフォルトでmulti_threadが選択されているようだ。
次に、tokio runtimeのmulti-threadがどうなっているか見ていく。
pub fn new_multi_thread() -> Builder {
// The number `61` is fairly arbitrary. I believe this value was copied from golang.
Builder::new(Kind::MultiThread, 61, 61)
}
new_multi_thread()はBuilderを返している。
pub fn build(&mut self) -> io::Result<Runtime> {
match &self.kind {
Kind::CurrentThread => self.build_current_thread_runtime(),
#[cfg(all(feature = "rt-multi-thread", not(tokio_wasi)))]
Kind::MultiThread => self.build_threaded_runtime(),
}
}
builderのbuild()を実行すると、この場合だとbuild_threaded_runtimeが実行される。
最終的に、
Runtime::from_parts(Scheduler::MultiThread(scheduler), handle, blocking_pool)
で、MultiThreadのSchedulerが返ることになる。
このSchedulerがマルチスレッドのスケジューリングを提供しているようだ。
この中でPark/Unparkをしている。