Help us understand the problem. What is going on with this article?

すごいE本 第17章 後半戦 on Elixir (DynamicSupervisor)

環境

sh
$ lsb_release -d
Description:    Ubuntu 18.04.2 LTS

$ elixir -v
Erlang/OTP 21 [erts-10.3.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Elixir 1.8.1 (compiled with Erlang/OTP 20)

17.3 バンドで練習

simple_one_for_one スーパバイザを使う

:simple_one_for_one のとき、子の仕様(全て一律)は辞書で保持されるらしい。
それ以外の戦略の場合はリストで保持されるので子が多い場合は前者がいいんだね。

Supervisor.start_child(sup, [arg, ...]) とすると、
:simple_one_for_onesup の子仕様 start: {mod, :start_link, args} を探し、
apply(mod, :start_link, args++[arg, ...]) を呼び出す。
ややこいね。

Elixir では非推奨だけどやってみよう。

band/lib/band.ex
defmodule Band do
  use Supervisor
  alias __MODULE__, as: Me
  alias __MODULE__.Musician
  ...
  # 追加
  @impl true
  def init(:jamband) do
    child = %{
      # 親が子を識別するための child_id
      id: :jam_musician,
      start: {Musician, :start_link, []},
      restart: :temporary,
    }

    Supervisor.init([child], strategy: :simple_one_for_one)
  end
  ...
end
iex
iex(1)> Band.start_link(:jamband)
{:ok, #PID<0.147.0>}
iex(2)> Supervisor.start_child(Band, [:djembe, :good])
Dorothy Frizzle(good djembe) は部屋に入った。
{:ok, #PID<0.149.0>}
Dorothy Frizzle(good djembe) の音はいいね!
...
iex(3)> Supervisor.start_child(Band, [:djembe, :good])
{:error, {:already_started, #PID<0.149.0>}}
...

Supervisor.start_chid(Band, [:djembe, :good]) が、
apply(Band.Musician, :start_link, []++[:djembe, :good]) を呼び出す。

Musician の定義を思い出すと、この :djembe で名前を登録するのであった。
なわけで、2回目の start_child/2 で名前衝突が起きている。
こういう状況に便利な Registry というものがあるが、
話が長くなるので、ここでは E本の流れに準じ単に違うアトムを渡す。

iex
...
iex(4)> Supervisor.start_child(Band, [:drum, :good])
Wanda Perlstein(good drum) は部屋に入った。
{:ok, #PID<0.152.0>}
...
iex(5)> Supervisor.start_child(Band, [:guitar, :good])
Phoebe Li(good guitar) は部屋に入った。
{:ok, #PID<0.154.0>}
...
iex(6)> Supervisor.terminate_child(Band, :djembe)
{:error, :simple_one_for_one} # あれれ?
...
iex(7)> Band.Musician.stop(:djembe)
Dorothy Frizzle(good djembe) は部屋を出た。
:ok
...

うまい話ばかりじゃないですよ

うーん、話が食い違うねー。
Erlang 20 でコンパイルされた Elixir なんだけど、どーして?

iex
...
iex(6)> Supervisor.terminate_child(Band, Process.whereis(:djembe))
マネージャーは怒りバンドを解散させた!
Dorothy Frizzle(good djembe) は地下鉄へ去っていった。

:ok

誤植に耐えてよく頑張った!感動した!

DynamicSupervisor を使ってみる

DynamicSupervisor — Elixir vx.x.x

Supervisor から :simple_one_for_one を排除したいのね。
別のモジュールにしちゃった方が見通しが良いでしょということかな。

sh
$ touch lib/band/jam.ex
band/lib/band/jam.ex
defmodule Band.Jam do
  use DynamicSupervisor
  alias __MODULE__, as: Me
  alias Band.Musician

  ### use で child_spec/1 が自動的に定義される。 ###
  # def child_spec(arg) do
  #   %{
  #     id: __MODULE__,
  #     start: {__MODULE__, :start_link, [arg]},
  #     type: supervisor
  #   }
  # end

  def start_link do
    DynamicSupervisor.start_link(Me, [], name: Me)
  end

  def stop, do: DynamicSupervisor.stop(Me)

  def start_child(role, skill) do
    child = %{
      id: Musician,
      start: {Musician, :start_link, [role, skill]},
      restart: :temporary
    }

    DynamicSupervisor.start_child(Me, child)
  end

  def terminate_child(role) do
    pid = Process.whereis(role)
    DynamicSupervisor.terminate_child(Me, pid)
  end

  @impl true
  def init([]) do
    # 子仕様リストとして返却しなくて済むようになった。
    DynamicSupervisor.init(strategy: :one_for_one)
  end
end
iex
iex(1)> Band.Jam.start_link()
{:ok, #PID<0.166.0>}
iex(2)> Band.Jam.start_child(:djembe, :good)
Carlos Frizzle(good djembe) は部屋に入った。
{:ok, #PID<0.168.0>}
Carlos Frizzle(good djembe) の音はいいね!
...
iex(3)> Band.Jam.start_child(:drum, :good)
Dorothy Li(good drum) は部屋に入った。
{:ok, #PID<0.170.0>}
...
iex(4)> Band.Jam.start_child(:guitar, :good)
Wanda Terese(good guitar) は部屋に入った。
{:ok, #PID<0.172.0>}
...
iex(5)> Band.Jam.terminate_child(:djembe)
マネージャーは怒りバンドを解散させた!
Carlos Frizzle(good djembe) は地下鉄へ去っていった。

:ok
...
iex(6)> Band.Jam.stop()
マネージャーは怒りバンドを解散させた!
Dorothy Li(good drum) は地下鉄へ去っていった。

マネージャーは怒りバンドを解散させた!
Wanda Terese(good guitar) は地下鉄へ去っていった。

:ok

うむ、言語設計者の哲学を感じるね。

動的に子を追加していく場合、今回の様にタプルで名前をつけていくのはよろしくない。
タプルはガベージコレクションの対象ではないからだ。
自前で対策を踏むよりも Registry を利用した方が良さそうだ。
だが、この章の段階では手を出すには早すぎるだろう。

次章からはアプリケーションだ。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした