tl;dr
ElixirのProcessとGo routineのデータの共有ポリシー, データの送受信のコードも似ているが, Elixirの全てのコードはProcessで動くため ProcessとGo routineの意味合いは違う。
比較するきっかけ
並行処理間のデータの共有のポリシーがElixirとGoで似ていると感じたため。
ElixirはProcess, Goには Go Routines 並行処理に適している仕組みがある。
他の言語では主にシェアメモリを利用して並行処理を実装すると, レースハザードがおきないようにメモリへのアクセス, 書き込みを制限しなければならず実装が難しい。
が Go, Elixir, Erlang(他の言語もあるはず)ではシェアメモリを使わずプロセス間でデータの送受信を行うため並行処理を実装しやすい。
Goの場合
参照リンク
GoではRoutine間のデータの共有はChannelを使用しプロセスにデータの参照を送る。
Goroutine とはなんぞや
- ThreadではなくGoランタイムで管理される軽量プロセスである。
- 関数が終わる, returnで処理を抜ける, runtime.Goexit() を実行したタイミングでGo Routineが終了する。
Channelとはなんぞや
- goroutine 間で値の送受信のために使用するもの。
- send =>
channel<-value
, receive =><-channel
実装例
package main
import "fmt"
func main() {
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
処理の流れ
-
make (chan string)
channelを作成。 -
go func() { messages <- "ping" }()
Go routineを起動し, channelに "ping"を送信します。 -
<-messages
でchannelからデータを受け取る。
Elixirの場合
ElixirはProcess間のデータの共有はプロセスのid(PID)を指定して send
, データを受け取るプロセスは recieve
で受け取る。
Processってなんぞや
Go Routineと違いElixirの全てのコードはProcess上で動く。
Process同士は独立,並行して動き,メッセージパッシングでやり取りする。
OSのプロセスとは違い, とても軽量なものである。
defmodule Spawn1 do
def greet do
receive do
{sender, msg} ->
send sender, { :ok, "Hello, #{msg}" }
end
end
end
pid = spawn(Spawn1, :greet, [])
send pid, {self, "World!"}
receive do
{:ok, message} ->
IO.puts message
end
1 pid = spawn(Spawn1, :greet, [])
spawn
関数は新しいプロセスを開始し, プロセスを一意に識別できるPID(プロセスID)を返す。
Spawn1モジュールのgreedメソッドを走らせる新しいプロセスを生成する。
2 send pid, {self, "World!"}
send
関数でメッセージを送る。引数としてPID, 送信するメッセージを指定する。
3 receive
receive を使ってメッセージを待つ。
これからやりたいこと
ベンチマークとったり, 並行処理モデルの違いについてまとめたい。
Go Routine, Channelについて参考にさせていただきました