プロセス

■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

	・お互いが終了
		プロセスが強制終了した場合、リンクしているプロセスは終了します。