スポンサーリンク

2015年8月15日

[Elixir]プロセスの基本を習得する

とある錬金術師の万能薬(Elixir)

Goal

Elixirプロセスの基本的な使い方を習得する。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4

Wait a minute

Elixirのプロセスを使ってみます。
プロセスを生成したり、メッセージを送ってみたりします。
Stateでは、状態について学びます。
また、Agentについても少しやります。

Index

Processes
|> For process in Elixir
|> Create process
|> Message process
|> Links
|> State
|> Extra

For process in Elixir

Elixirでは全てのコードがプロセス内部で動いている。
特徴のまとめ・・・
  • プロセス同士は独立している
  • プロセスは並行で動作する
  • メッセージパッシングでやり取りする
  • OSのプロセスとは別
  • メモリとCPUに大して軽量
分散処理や高可用性の構築に役立つそうです。
数千のプロセスを保持できるとか・・・素晴らしい!!

Create process

プロセスの生成について。
spawn/1について簡単なまとめ・・・
  • 他のプロセスで実行する関数を受け取る
  • PID(プロセス識別子)を返す
  • 関数を実行したら死ぬ
プロセスを生成してみる。
iex> pid = spawn fn -> 1 + 1 end
#PID<0.58.0>
プロセスが生きているか確認してみる。
iex> Process.alive?(pid)
false
今のプロセス(PID)を取得してみる。
iex> self
#PID<0.56.0>

Message process

プロセス間でのメッセージをやり取りについて。
簡単なまとめ・・・
  • send/2を使って送る
  • receive/1を使って受け取る
  • flush/0で全てのメッセージを表示して空にできる
実際に使ってみる。
メッセージのやり取りをしてみる。
iex> parent = self
iex> spawn fn -> send(parent, {:pid, self}) end
#PID<0.98.0>
iex> receive do
...>   {:pid, pid} -> "receive pid = #{inspect pid}"
...> after
...>   1_000 -> "nothing after 1s"
...> end
"receive pid = #PID<0.98.0>"
Description:
以下の部分でメッセージがない場合のタイムアウトを設定している。
...> after
...>   1_000 -> "nothing after 1s"
...> end
flush/0で表示させてみる。
iex> spawn fn -> send(parent, {:pid, self}) end
#PID<0.105.0>
iex> flush
{:pid, #PID<0.105.0>}
:ok
spawn_link/1を経由させてみる。
また、プロセスの失敗を見てみる。
簡単なまとめ・・・
  • プロセスは独立している
  • だから、他のプロセスに影響がない
  • 伝播させるには、spawn_link/1を使う
  • link/1で手動でも行える
  • Supervisorがシステムの再起動をしてくれる
  • さっさとクラッシュさせる方針が取れる
失敗させてみる。
iex> spawn fn -> raise "oops" end
#PID<0.58.0>

23:06:25.413 [error] Error in process <0.58.0> with exit
value: {#{'__exception__'=>true,'__struct__'=>'Elixir.RuntimeError',message=><<4 bytes>>},[{erlang,apply,2,[]}]}
spawn_link/1を使ってみる。
iex(1)> spawn_link fn -> raise "oops" end

23:28:23.819 [error] Error in process <0.58.0> with exit
value: {#{'__exception__'=>true,'__struct__'=>'Elixir.RuntimeError',message=><<4 bytes>>},[{erlang,apply,2,[]}]}

** (EXIT from #PID<0.56.0>) an exception was raised:
    ** (RuntimeError) oops
        :erlang.apply/2

Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

State

アプリケーション設定や解析データなどの保持について。
実際に、elixir-lang - Processes - Stateのサンプルを試して経験してみます。
defmodule KV do
  def start_link do
    {:ok, spawn_link(fn -> loop(%{}) end)}
  end

  defp loop(map) do
    receive do
      {:get, key, caller} ->
        send caller, Map.get(map, key)
        loop(map)
      {:put, key, value} ->
        loop(Map.put(map, key, value))
    end
  end
end
実行してみる。
>iex kv.exs
iex> {:ok, pid} = KV.start_link
{:ok, #PID<0.62.0>}
iex> send pid, {:put, :hello, self}
{:put, :hello, #PID<0.60.0>}
iex> send pid, {:get, :hello, self}
{:get, :hello, #PID<0.60.0>}
iex> flush
#PID<0.60.0>
:ok
pidに名前を付けてみる。
(上記のpidを使っています)
iex> Process.register(pid, :kv)
true
iex> send :kv, {:get, :hello, self}
{:get, :hello, #PID<0.60.0>}
iex> flush
#PID<0.60.0>
:ok
ここら辺を上手く抽象化してくれているのがAgentらしい。

Extra

先ほどと、ほぼ同一の内容をAgentを使って少しだけやってみます。
iex> {:ok, pid} = Agent.start_link(fn -> %{} end)
{:ok, #PID<0.67.0>}
iex> Agent.update(pid, fn map -> Map.put(map, :pid, self) end)
:ok
iex> Agent.get(pid, fn map -> Map.get(map, :pid) end)
#PID<0.67.0>

Speaking to oneself

正直に言いますが、何に使えるのか自分の実力ではさっぱりです。
(パラレルダウンロード、平行で解析を流す・・・とかですかね?)
まぁ、プログラムっぽくて割と好きな内容でしたけど(笑)

Bibliography

人気の投稿