GoとRust - 並行プログラミング編

22973 ワード

はじめに

こんにちは です。

goroutine何も分からん!async/await何も分からん!となったのでそれぞれを比較しつつ理解を深めてみよう。という考えのもとGo, Rustの並行プログラミングの解説記事を書いてみました。

ところどころふわっとしているため、補足や指摘を貰えると大変助かります。

今回話すこと

  • goroutineとは結局何なの
  • Goの並行処理の仕組み
  • goroutine(Go)とasync/await(Rust)の比較
  • Goのランタイム、Rustのランタイムの話

話さないこと

  • 構文の違いについては特に触れない
  • どちらが優れているとい言う話はしない
  • ベースになっている思想については特に触れない

TL;DR

  • Goには標準のランタイムがあるよ、Rustではランタイムライブラリを使う必要があるよ
  • Goはランタイムが中断再開を管理するよ、Rustではプログラマーが管理するよ
    • ブロッキング処理を混入しちゃうと危険だね
    • Goはプリエンプティブだよ, Rustだとノンプリエンプティブだよ
  • どっちもタスクのスケジューリングはワークスティーリングという戦略だよ
    • Goはタスク本体ではなく継続を盗み合っているよ(継続ワークスティーリング)
    • Rustはタスク本体を盗み合っているよ (チャイルドワークスティーリング)

Go, Rustでの並行プログラミング

まずは簡単にGo, Rustで並行プログラミングをやってみましょう。

Goであればgoキーワードを書くだけで簡単に非同期実行されるタスク(以下非同期タスクと呼ぶことにします)を起動できますね。

package main
import "fmt"

func main() {
	f := func() { fmt.Println("hoge") }
	go f() // Goランタイムがいい感じに処理してくれる
}

このとき生成したタスクは自動的にランタイムに渡され、ランタイムが効率的に実行してくれています。
ランタイムが担っているタスクのスケジューリングについては後から見ていきましょう!

続いてRustの方はどうでしょうか?

RustではGoと違ってランタイムが標準でサポートされていないためタスクの実行ができません。(Rustはタスクの起動や型などは規定されていますが、どのように実行するかという部分は対象としていません)

タスクを起動するためにはランタイムライブラリを用いる必要があります。本記事ではtokioというランタイムライブラリを使用します。「Rustでは」と言ったときは殆どの場合が「Rustのランタイムのtokioでは」という話になります。

tokioを用いたタスクの起動は次のようになっています。現状tokio::spawngoキーワードと同じものと考えて問題ないと思います。

tokio::spawnを使うことで非同期実行されるタスクを起動して、実行タイミングをランタイムに任せることが出来ています。

#[tokio::main] // ランタイムを起動するおまじない
async fn main() {
    let f = || async { println!("hoge") };

    // tokio spawnで起動している
    tokio::spawn(f());
}

タスクについて

gotokio::spawnで起動されたナニカを単にタスクと呼んでいますが適切な名前なんでしょうか?とりあえず本記事ではこれらの総称をタスクとします。

ここからはGoで起動したタスクをgoroutine、Rustで起動したタスクを非同期タスクと呼び比較してくことにします。goroutineは一般的な名前ですがRustでは固有の名前が無いようなので非同期タスクと命名しておきます。(roroutineとかにしようと思ったけど紛らわしいし、ゴルーチンに合わせに行く意味もないので止めておきました 🙈 )

goroutine

ではgoroutineについて見ていきましょう。

一言でいうと、goroutineはコルーチンをプログラマーが扱いやすいように抽象化したものです。

うーん。なるほど?🤔

じゃあコルーチンとは何だ?となったので一旦コルーチンの話をします。

コルーチン

コルーチンの雰囲気がわかれば十分だと思うので概要だけを話します。

通常の関数は呼び出し後は最後まで処理を継続しますが、コルーチンは呼び出した後に処理を途中で中断し後から再開することが出来ます。

次の図を見るとイメージつきやすいかなと思います

goroutineも非同期タスクも中断と再開を繰り返しているためコルーチンの一種と言えそうですね。

コルーチン詳細な説明についてはこちらのスライドが参考になります。コルーチンをちゃんと理解したい人はこちらを!!