第72回です。前回はこちら。
[第72回の様子]
2023/03/29に第72回を開催した。
内容としてはRust By Example 日本語版20. 標準ライブラリのその他の「20.2. チャネル
」に取り組んだ。
参加者は自分を入れて6人。参加してまだ日の浅いメンバがドライバやってくれて、復習しながら進められたのでありがたかった!
[学んだこと]
- 20.2. チャネル
- スレッド間のコミュニケーションのために非同期のchannelを利用できる
- SenderとReceiverの間に一方通行のやりとりができる
- チャネルを生成するのは以下のようなコード
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
// SenderやReceiverにつくジェネリクスは送受信されるデータの型
let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();
- mpscって何だろう、と思ったけど、参加者が「multi producer single consumer」と教えてくれた。公式のドキュメントにもそう書いてあった。
- FIFOなキューかどうかが気になってたけど、それも公式ドキュメントに書いてあった
- 作成したSenderでデータを送信するには以下のようにする
use std::thread;
static NTHREADS: i32 = 3;
let mut children = Vec::new();
for id in 0..NTHREADS {
// 送信者エンドポイントをコピー
let thread_tx = tx.clone();
// スレッドを起動しメッセージを送信
let child = thread::spawn(move || {
// Resultが返ってくるのでエラーだったらpanicするためにunwrap()しておく
thread_tx.send(id).unwrap();
println!("thread {} finished", id);
});
// スレッドの終了を待機させるためにJoinHandleをベクタに詰めておく
children.push(child);
}
// スレッドの終了を待機
for child in children {
child.join().expect("oops! the child thread panicked");
}
- Receiver側は以下のようになる
let mut ids = Vec::with_capacity(NTHREADS as usize);
for _ in 0..NTHREADS {
// 取り出せるメッセージが存在しない場合、
// recv()メソッドは現在のスレッドをブロックする。
ids.push(rx.recv());
}
// 結果を出力
println!("{:?}", ids);
// 以下のようにスレッドの実行順に出力される
[Ok(1), Ok(0), Ok(2)]
- 公式ドキュメントを見ている内に、Receiverがドロップされていたらメッセージ送信がエラーになると書かれているのを見つけた
- ので、実験してみた
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();
// レシーバをドロップ
drop(rx);
tx.send(4).unwrap();
// 以下のようにpanicする
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SendError { .. }', src/main.rs:62:16
- SendErrorというエラーが返ってくるらしい
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
Goにも似たようなチャネルの機能があって、そちらを知っていたので今日の内容は理解しやすかった気がする。
今週のプルリクエストはこちら。