Rustで並列処理をやりましょう。
恐れるな!並行性 基本的にはここを読めばできます。
thread::spawn
を使ったりなんやかんやするのですが、今回はtokio::spawn
の方を使います。
使い方
use tokio::time::{sleep, Duration} #[tokio::main] fn main() { let handle1 = tokio::spawn(async { sleep(Duration::from_secs(5)).await; println!("Task1 !!!"); }); let handle2 = tokio::spawn(async { sleep(Duration::from_secs(2)).await; println!("Task2 !!!"); }); // それぞれの終了を待つ handle1.await.unwrap(); handle2.await.unwrap(); }
これだけ。
普通にthread::spawn
と書き方もあんまり変わらないですね。
それぞれ、クロージャ外で定義した変数を使いたい場合は、move
で、クロージャに所有権を移します。
use tokio::time::{sleep, Duration} #[tokio::main] fn main() { let message1 = "Task1!"; let message2 = "Task2!"; let handle1 = tokio::spawn(async move { sleep(Duration::from_secs(2)).await; println!("Task1-1: {}", Local::now()); sleep(Duration::from_secs(3)).await; println!("{}", message1); }); let handle2 = tokio::spawn(async move { sleep(Duration::from_secs(2)).await; println!("{}: {}", message2, Local::now()); }); // それぞれの終了を待つ handle1.await.unwrap(); handle2.await.unwrap(); println!("Hello!"); }
Task1-1, Task2の表示がほぼ同時にくることが分かると思います。
同時に動いていることがわかります。
実用的なやつ
さて、おそらくこういったことが実際使いたい場面は、ループが絡んでそうです。
async fn async_fn(arg: i32) -> Result<()> { // なんかかする Ok(() } #[tokio::main] async fn main() { let items = // 何かしら読み込む itemsはVec<T> let tasks = new Vec(); // Vec<JobHandle> for item in items { let task = tokio::task::spawn(async move { async_fn(&item.id).await; }); tasks.push(task); } for task in tasks { match task.await { Ok(_) => println!("Success!"), Err(e) => println!("{}", e) } } }
こんなイメージで使えるかと。
DBから並列でデータとってきてログうんぬんのやつなら、
let db_connections: Vec<Connection> = // ... let tasks = new Vec(); // Vec<Jobhandle> for connection in db_connections { let task = tokio::task::spawn(async move { fetch_log(&connection).await; }); tasks.push(tesk); } // ...
的なイメージになると思います。
ただ、この場合は、並列でとったデータをうまい具合に結合してあげて...。
みたいなのもいりそう。
https://doc.rust-lang.org/std/future/trait.Future.html
こういうの使って、join_all
でまとめて、どうこうとか。
そういう感じかもしれないです。(試してない)
一応Rustで並列処理が書けるようになりました。