プロセス
■spawn
プロセスの作成方法の中でもっとも簡単な方法として「spawn」があります。
新しいプロセスが作成されるとプロセスの識別子であるPIDが返ります。
このPIDはユニークな値になっており、保持中のプロセスの識別に使用します。
●書式(関数名指定)
・書式例
spawn(モジュール名, :関数名, [引数])
※関数名は「:」をつける必要があります。
・具体例
defmodule SpawnTest do
def greet(message) do
IO.puts message
end
end
# プロセス作成
spawn(SpawnTest, :greet, ["Hello World"])
●書式(匿名関数)
・書式例
spawn fn -> 処理 end
・具体例
spawn fn -> IO.puts ("Hello World") end
■メッセージの送受信
プロセス間で通信の送受信をするには「send」と「receive」を使用します。
送信が「send」受信が「receive」で、通信にはspawnを実行した際に返されるPIDが必要です。
●送信
sendには送信先PIDと送信データを引数に指定します。
この時の引数にはタプルを使用して「{ 種類(アトム型), 値 }」の形式にする
ケースが多いようです。
・書式例
send(送信先のPID, 送信データ)
・具体例
defmodule CommunicationTest do
def greet do
receive do
msg -> IO.puts msg
end
end
end
pid = spawn(CommunicationTest, :greet, [])
send(pid, "Hello World")
結果:
Hello World
●受信
上の送信ですでに使用していますが、receiveはcaseのようにパターンマッチを
複数コーディングし、最初に当てはまったパターンが実行されます。
・書式例
receive do
パターンマッチ -> 処理
end
・具体例
defmodule CommunicationTest do
def greet do
receive do
:morning -> IO.puts "Good Morning"
:noon -> IO.puts "Hello"
:evening -> IO.puts "Good Evening"
_ -> "not match"
end
end
end
pid = spawn(CommunicationTest, :greet, [])
send(pid, :morning)
結果:
Good Morning
・receiveの挙動
receiveはsendからのデータを受信するまで待機状態になっています。
・例(send無し)
defmodule CommunicationTest do
def greet do
IO.puts "greet start"
receive do
:morning -> IO.puts "Good Morning"
:noon -> IO.puts "Hello"
:evening -> IO.puts "Good Evening"
_ -> "not match"
end
IO.puts "greet end"
end
end
spawn(CommunicationTest, :greet, [])
結果:
greet start
上の例のようにspawnを使用した段階で、関数が呼び出されますが、
receive以降のコードは実行されず、待機状態になります。
sendにより、データを受信したら、処理が再開され、最後まで実行されます。
・例(send有り)
defmodule CommunicationTest do
def greet do
IO.puts "greet start"
receive do
:morning -> IO.puts "Good Morning"
:noon -> IO.puts "Hello"
:evening -> IO.puts "Good Evening"
_ -> "not match"
end
IO.puts "greet end"
end
end
pid = spawn(CommunicationTest, :greet, [])
send(pid, :noon)
結果:
greet start
Hello
greet end
●プロセスの複数回使用
プロセスは関数が実行されると自身を廃棄するので複数回使用できません。
・例(プロセス廃棄確認)
defmodule CommunicationTest do
def greet do
receive do
:morning -> IO.puts "Good Morning"
:noon -> IO.puts "Hello"
:evening -> IO.puts "Good Evening"
_ -> "not match"
end
end
end
pid = spawn(CommunicationTest, :greet, [])
send(pid, :morning)
send(pid, :noon)
結果:
Good Morning
上の例ではプロセスに「:morning」と「:noon」を送信していますが、
結果は「Good Morning」しか出力されません。
これは最初のプロセスが最初のsendの処理を実行した際にプロセスを終了しているので
2回目のsendが無効になっています。
複数回プロセスを使いたい場合は関数の最後で再帰呼び出しを行います。
・例(再帰使用による複数使用)
defmodule CommunicationTest do
def greet do
receive do
:morning -> IO.puts "Good Morning"
:noon -> IO.puts "Hello"
:evening -> IO.puts "Good Evening"
_ -> "not match"
end
# 再帰追加
greet()
end
end
pid = spawn(CommunicationTest, :greet, [])
send(pid, :morning)
send(pid, :noon)
結果:
Good Morning
Hello
関数の最後で再帰を行うことで、再度recieveの待機状態になるので、
プロセスを使い回すことができるようになります。
●送受信
送信先の結果を受信したい場合は「self」を使用して自身のpidを送信時に渡します。
・例
defmodule CommunicationTest do
def add do
receive do
{ send_pid, val1, val2 } ->
send(send_pid, { :ok, (val1 + val2) } )
_ -> { :ng, IO.puts "not match" }
end
add()
end
end
pid = spawn(CommunicationTest, :add, [])
send(pid, { self(), 1, 2 })
receive do
{ :ok, val } -> IO.puts "add => #{val}"
end
selfは動作中のプロセスのPIDを返す関数なので、これを使用して
sendにどのプロセスから送信しているのかを伝えれば送受信が可能となります。
■リンク
リンクはプロセス間で終了通知の受け取りをするための機能です。
デフォルトではプロセスの終了は誰にも通知されないので、別プロセスが強制終了になった場合は
何も知らずに受信待ちをし続けることになります。
これを回避するためにリンクを行います。
●リンク設定無しの例
以下はプロセス間でリンク設定を行っていないコードの例です。
greetは強制終了しているのに受信待ちをし続けています。
・例
defmodule CommunicationTest do
def greet do
exit(:boom)
end
end
pid = spawn(CommunicationTest, :greet, [])
send(pid, { self(), "Hello" })
receive do
msg -> IO.puts "greet message => #{msg}"
end
結果:
フリーズ
●リンク設定
プロセスをリンクさせるには「spawn_link」を使用します。
引数はspawnと同じなので、使用は難しくありません。
・書式例
spawn(モジュール名, :関数名, [引数])
・具体例
defmodule CommunicationTest do
def greet do
exit(:boom)
end
end
pid = spawn_link(CommunicationTest, :greet, [])
send(pid, { self(), "Hello" })
receive do
msg -> IO.puts "greet message => #{msg}"
end
結果:
** (EXIT from 終了したPID) :boom
・お互いが終了
プロセスが強制終了した場合、リンクしているプロセスは終了します。