非同期プログラミング on Rust のために知っときたいこと [メモ]
非同期プログラミング on Rust
このメモは「非同期? なんそれ?」っていう初心者がメモ用で書いたものです。 用語の誤用、誤った記述があると思います。ので、コメントで間違ってるところを教えていただけると嬉しいです。
Rustのstableにasync/awaitがもうすぐ入るようなので、非同期プログラミング,Futureについて勉強していこうと思います。 それのメモがこれです。
非同期プログラミング
非同期プログラミングとは?みたいなところから書いてきます。
非同期プログラミングは複数タスクを経公的に処理(実行)するための技法。
Webサーバーの例でいうと、1つのクライアントとの通信が終わるのを待たずに、他のクライアントの処理を開始する。これが非同期
Future
FutureトレイとはRustの非同期プログラミングの主役的存在です。 jsのPromiseみたいなイメージかな
以下のような定義になってます。
pub trait Future { type Output; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> } pub enum Poll<T> { Ready(T), Pending } pub struct Context<'a> { /* fields omitted */ } impl<'a> Context<'a> pub fn from_waker(waker: &'a Waker) -> Context<'a>; pub fn waker(&self) -> &'a Waker; } pub struct Waker { /* fields omitted */ } impl Waker { pub fn wake(self); pub fn wake_by_ref(&self); pub fn will_wake(&self, other: &Waker) -> bool; pub unsafe fn from_raw(waker: RawWaker) -> Waker; }
このような実行モデルは、ポーリングモデルと呼ばれています。
具体的には、pollメソッドはself
とコンテキストcx
を受け取り、Poll<Self::Output>
型を返します。
このPollっていうのはReady
とPending
の2つの状態のどちらかを表すことができます。
Ready
バリアントは計算完了を表し、計算結果の値を持ちます。
Pending
は呼んでのごとく、完了していないことを表します。
少し戻りますが、pollの引数が&mut self
ではなくself: Pin<&mut self>
になっています。 これは自己参照構造体を実現するためです。 詳しくはボクも分かってないのでここでは説明できません。雑に記事を貼っておきます。
https://qiita.com/ubnt_intrepid/items/df70da960b21b222d0ad
Poll実行タイミング
Futureの所有者はいつpollを呼び出すのか?という問題があるかと思います。
pollを呼び出してPending
が返ってきたとします。そのようなFutureに対しまたすぐにpollを呼び出すのではCPUを無駄に使用する可能性が高いですね。
なぜなら、Pending
が返ってきているので、計算が終了するまでには、もう少し時間がかかる可能性が高いので。
ここで登場するのが先程しれっと書いたWaker
なのですが詳細な話は省こうと思います。
とりあえずWaker
のwakeメソッドを呼び出すことでランタイムにタスクの再開通知ができます。
まとめ
一旦終わりです。 肝心のAsync/awaitには入ってないですが、基本的なところはこんな感じなので。
あとから、ジェネレーターやAsync/awaitの話を追記しようと思いますが、とりあえずリリースしようぜの精神でpostします。