今年の言語はRust その63
Rustを学びます
Rustの日本語ドキュメント 2nd Edition
https://doc.rust-jp.rs/book/second-edition/
オリジナル(英語)
https://doc.rust-lang.org/book/
実行環境
$ cargo -V
cargo 1.33.0 (f099fe94b 2019-02-12)
$ rustup -V
rustup 1.17.0 (069c88ed6 2019-03-05)
$ rustc --version
rustc 1.33.0 (2aa4c46cf 2019-02-28)
$ cat /proc/version
Linux version 4.14.97-74.72.amzn1.x86_64 (mockbuild@gobi-build-64002)
(gcc version 7.2.1 20170915 (Red Hat 7.2.1-2) (GCC))
# 1 SMP Tue Feb 5 20:59:30 UTC 2019
$ uname -a
Linux ip-10-100-0-8 4.14.97-74.72.amzn1.x86_64
# 1 SMP Tue Feb 5 20:59:30 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/\*release
NAME="Amazon Linux AMI"
VERSION="2018.03"
ID="amzn"
ID_LIKE="rhel fedora"
VERSION_ID="2018.03"
PRETTY_NAME="Amazon Linux AMI 2018.03"
ANSI_COLOR="0;33"
CPE_NAME="cpe:/o:amazon:linux:2018.03:ga"
HOME_URL="http://aws.amazon.com/amazon-linux-ami/"
Amazon Linux AMI release 2018.03
20. マルチスレッドのWebサーバーを構築する
やっとここまで来ました。最後の章です。
実はWebサーバーの仕組みが知りたくて知りたくて60回もやってきました。
以下がレッスン内容になります
- TCPとHTTPについて少し学ぶ。
- ソケットでTCP接続をリッスンする。
- 少量のHTTPリクエストを構文解析する。
- 適切なHTTPレスポンスを生成する。
- スレッドプールでサーバのスループットを強化する。
20.3 優雅なシャットダウンと片付け
ThreadPoolにDropトレイトを実装する
/// Dropの実装
impl Drop for ThreadPool {
fn drop(&mut self) {
for worker in &mut self.workers {
println!("Shutting down worker {}", worker.id);
worker.thread.join().unwrap();
}
}
}
まず、これはコンパイルエラーになる
>cargo check
Checking hello v0.1.0 (H:\Dropbox\07_DevelopFolda\20190502_Rust\hello)
error[E0507]: cannot move out of borrowed content
--> src\lib.rs:71:4
|
71 | worker.thread.join().unwrap();
| ^^^^^^^^^^^^^ cannot move out of borrowed content
error: aborting due to previous error
For more information about this error, try `rustc --explain E0507`.
error: Could not compile `hello`.
To learn more, run the command again with --verbose.
workerへは可変参照しかない。join()は所有権を奪うので、エラーとなる。
Workerの視点で考えると。Worker.threadはNoneを許容できない。
つまり、join()により所有権が奪われるとthread=Noneとなってしまうのでコンパイラに怒られるのだ。
Worker.threadにNoneを許すのであれば、Optionを使用するのがよい。
struct Worker {
id: usize,
thread: thread::JoinHandle<()>,
}
書き換える
struct Worker {
id: usize,
thread: Option<thread::JoinHandle<()>>,
}
Workerの生成時にSomeで囲います
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>> ) -> Worker {
// -- snip --
Worker {
id,
thread: Some(thread)
}
}
}
Option型なので、if let構文を使って値を取り出します。
Noneの場合は、動きません。C++等でおなじみのnull回避のケースですね。
/// Dropの実装
impl Drop for ThreadPool {
fn drop(&mut self) {
for worker in &mut self.workers {
println!("Shutting down worker {}", worker.id);
if let Some(thread) = worker.thread.take() {
thread.join().unwrap();
}
}
}
}
スレッドに仕事をリッスンするのを止めるように通知する
/// スレッドの動作についての通知
enum Message {
NewJob(Job),
Terminate,
}
pub struct ThreadPool{
workers: Vec<Worker>,
sender: mpsc::Sender<Message> // Messageを送るSenderのこと
}
impl ThreadPool {
pub fn execute<F>(&self, f: F)
where F: FnOnce() + Send + 'static
{
let job = Box::new(f);
// [-] self.sender.send(job).unwrap();
self.sender.send(Message::NewJob(job)).unwrap(); // [+]
}
}
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>> ) -> Worker {
let thread = thread::spawn(move || {
loop {
let message = receiver.lock().unwrap().recv().unwrap();
// receiverにより、(senderから)受け取ったメッセージにより動作を決める
match message {
Message::NewJob(job) => {
println!("Worker {:?} got a job; executing.", id);
job.call_box();
},
Message::Terminate => {
println!("Worker {:?} was told to terminate", id);
break; // ループ終了
}
}
}
});
Worker {
id,
thread: Some(thread)
}
}
}
まとめ
サーバーの実装を甘く見積もっていた。
ほとんどは非同期処理についてでした。
サンプルを通しただけではうまく理解できなかった
自転車本が届いたので、別の側面から勉強を続けようと思う。
以上!
あと、5/8にRustの最新本がでるみたいです! 気が付いてラッキー!購入しました。 (現在5/9)
実践Rust入門[言語仕様から開発手法まで] 単行本(ソフトカバー) – 2019/5/8
※アフェっときます