More than 1 year has passed since last update.
Rust 100 Ex 🏃【35/37】 非同期ランタイム・Futureトレイト ~非同期のお作法~
前の記事
- 【0】 準備 ← 初回
- ...
-
【34】
async fn・非同期タスク生成 ~Rustの非同期入門~ ← 前回 - 【35】 非同期ランタイム・Futureトレイト ~非同期のお作法~ ← 今回
100 Exercise To Learn Rust 演習第35回になります!
8章の非同期について、全体的に筆者の理解度が低いです...他の本とかも読んでる最中...マサカリ大歓迎です!
記事は執筆時点での理解という感じです
今回の関連ページ
[08_futures/03_runtime] 非同期ランタイム tokio (もとい演習自体は Arc の使用)
問題はこちらです。
// TODO: Implement the `fixed_reply` function. It should accept two `TcpListener` instances,
// accept connections on both of them concurrently, and always reply clients by sending
// the `Display` representation of the `reply` argument as a response.
use std::fmt::Display;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpListener;
pub async fn fixed_reply<T>(first: TcpListener, second: TcpListener, reply: T)
where
// `T` cannot be cloned. How do you share it between the two server tasks?
T: Display + Send + Sync + 'static,
{
todo!()
}
前回 解説した通り、 非同期処理は Send を要求 します!そして、 reply: T を複製せずに 複数タスク(複製スレッド)で所有したい とくれば...?
独り言...「「「前回作成した図、今回載せれば良かった...!!!!」」」 というわけで前回の第34回も合わせて読んでほしいです 👁 :bow:
第34回に合わせて図を作成してしまい、作り直す体力がないです... 👁 :sweat_smile:
解説
そうですね、 Arc の出番です!というわけで、非同期処理でもお世話になりましょう。
( 次回 にて非同期専用の Mutex がある話等をするつもりですが、)このように、非同期処理も並行処理の一種で、 Send の制約がある仲間ですから、まるまる7章で学んだ知識が活かせます!
use std::sync::Arc;
pub async fn fixed_reply<T>(first: TcpListener, second: TcpListener, reply: T)
where
// `T` cannot be cloned. How do you share it between the two server tasks?
T: Display + Send + Sync + 'static,
{
let reply = Arc::new(reply);
let reply2 = Arc::clone(&reply);
tokio::spawn(listen(first, reply));
tokio::spawn(listen(second, reply2));
}
async fn listen<T>(listener: TcpListener, reply: Arc<T>)
where
T: Display + Send + Sync + 'static,
{
loop {
let (mut socket, _) = listener.accept().await.unwrap();
let reply = Arc::clone(&reply);
tokio::spawn(async move {
let reply = reply.to_string();
socket.write_all(reply.as_bytes()).await.unwrap();
socket.flush().await.unwrap();
});
}
}
前回自分は「tokioは複数スレッドを管理する」と言ったのですが、今回の Book には「マルチスレッドとシングルスレッドの2つのフレーバーがあり選べて、 #[tokio::main] マクロを使った場合はマルチスレッド、 #[tokio::test] マクロを使用した場合はシングルスレッドになる」と解説されていたりします。
デッドロックってマルチスレッド版だと気づきにくかったりしますし、テストがシングルスレッドなのは中々ありがたいですね。
その他、「 tokio::spawn は(スコープ async {} を跨ぐ値について1) 'static を要求する」とありますが、こちらは std::thread::spawn と同様ですね。タスクがいつまで生きているかはわかりません!
[08_futures/04_future] FutureトレイトとSendトレイト
問題はこちらです。久々のコンパイルを通してください問題になります。
//! TODO: get the code to compile by **re-ordering** the statements
//! in the `example` function. You're not allowed to change the
//! `spawner` function nor what each line does in `example`.
//! You can wrap existing statements in blocks `{}` if needed.
use std::rc::Rc;
use tokio::task::yield_now;
fn spawner() {
tokio::spawn(example());
}
async fn example() {
let non_send = Rc::new(1);
yield_now().await;
println!("{}", non_send);
}
しまった...こっちも前回解説してしまいました... 👁 :sweat_smile:
Send ではない値は .await を跨げません 。
解説
Send を実装していない型であったところで、 .await より前にドロップしてしまえば問題ないので、そのように書いちゃりましょう!
async fn example() {
{
let non_send = Rc::new(1);
println!("{}", non_send);
}
yield_now().await;
}
{} 内に収めることで Rc を .await 前にドロップさせています。 drop 関数等を使っても良いかもしれません。
その他 Book で取り上げられている大きめなトピックとして、 async fn の正体、 Future トレイトの話があります。Rustでは非同期もトレイトで説明されるのです!
async fn xxx(...) -> T {
// ...
}
↓
fn xxx(...) -> impl Future<Output = T> {
// ...
}
※ 手書きの場合は + Send + 'static みたいな形でさらに追加で色々付くことも...?また脱糖についての正確性は調べられてないです( cargo expand でわかるのかな...?)
「 Future トレイトを実装した型は ステートマシンである 」という内容を始めとし、かなり興味深い説明が書かれているので是非読んでほしいです!(筆者に説明の力量がない定期)
前回「 .await を跨ぐには Send が要る(キリッ」と筆者はぼかしながらテキトーに話していましたが、ちゃんとこれに対する理由も Future トレイトを実装した列挙体を例に挙げ説明されていたりします。
Futureって200通りあんねん...
上記の impl Future 脱糖について、たまに型を手書きする必要があったりして、その時に筆者がよくお世話になっているQiita記事様のリンクを掲載させていただきます 👁 :bow:
https://qiita.com/legokichi/items/4f2c09330f90626600a6
いや、パターン多すぎるだろ...非同期への理解もかなり深まりそうです
では次の問題に行きましょう!
次の記事: 【36】 ブロッキング・非同期用の実装・キャンセル ~ラストスパート!~
-
ローカルで生成される値への参照等であれば
'staticでなくとも.awaitは跨げるようです。 Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9ca535b6f390d2ae81cdc7b3c3cd39a6 ↩
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme
