祝!Rust 1.39.0
ついに、Rust 1.39.0になりました!そこで、既に他の方も書かれているかもしれないですが、安定化したasync
/.await
を試してみたいと思います(なお、筆者はRust及び非同期初心者です)。
基本的なasync
/.await
最も基本的な使い方は、
async fn f() {
let future = /* impl Futureな何か */;
future.await;
}
です。簡単!と思ったのですが、.await
はasync
関数またはブロックの中でしか呼べず、また.await
をしないと実際には処理を開始してくれないので、(簡単には)async
でないmain
関数から呼び出せません...困った。
futures
crateとの併用
futures 0.3
を使えば簡単にasync/.await
できるみたいなので、試してみました。
基本
まず、async fn
で非同期処理したい関数を作成します(ここまではstd
でできます。)
async fn long_task(task_id: usize) {
println!("Task {} on {:?}", task_id, std::thread::current().id());
std::thread::sleep(std::time::Duration::from_secs(1));
println!("Task {} done", task_id);
}
追記: 今更ながらちゃんとドキュメントをみたら std::thread::sleep
はasync fn
内では使ってはいけない と書いてありました。同様のことを行うには各runtime(tokioやasync_std、futuresの場合はfutures-timerというクレート)のDelay
を使うとできるそうです。
続いてこれをmain
関数から呼び出すわけですが、ここでfutures::executor::block_on
を使います。
fn main() {
let task = long_task(0); // ここではまだ実行されない
futures::executor::block_on(task); // ここで実行される(.awaitの代わりのようなもの)
}
これで main
関数と同じスレッドから long_task
関数が呼び出せました。
別スレッドでの処理
非同期処理するからには異なるスレッドで実行したいので、それを試してみます。そのためにまずfutures
にthread-pool
featureを加えて、futures::executor::ThreadPool
を作ります。ビルダーから作っても良いですし、単にnew
するだけでも大丈夫です。
別スレッドで処理してもらうためにspawn
系を呼び出すのですが、単にspawn
してそのまま終わってしまうとやはり実行されない(ことがある)ので、後でjoin
するために今回はspawn_with_handle
を用いました。その結果、以下のような感じになりました。
use futures::{
executor::{block_on, ThreadPool},
future,
task::SpawnExt,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut pool = ThreadPool::new()?;
let mut futures = Vec::new();
for i in 0..20 {
futures.push(pool.spawn_with_handle(long_task(i))?);
}
block_on(future::join_all(futures));
Ok(())
}
感想
現状、std
だけだと非同期処理を書くのはちょっと大変そうです。もちろん、futures
crateもRustで書かれているので頑張れば以上のことをstd
だけでできるはずですが、ソースコード見ても私にはなかなか難しそうだったので、素直に外部crateを使うことにします。ありがたや~