非同期プログラミング 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っていうのはReadyPendingの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します。