elixir 自己是一種 immutable 的語言,默認狀況下,進程間是不共享任何狀態的,進程之間經過消息來交互。
而 Agent 則封裝了一種進程間共享狀態的方式,經過這種方式,不用顯式的寫 send/receieve 的代碼,就能方便的在進程之間共享狀態。html
首先,看一個在不用 Agent 的狀況下,如何獲取進程狀態的例子。測試
defmodule WithoutAgent do def start do Map.new() end def get(map, key) do if Map.has_key?(map, key) do Map.get(map, key) else nil end end def put(map, key, val) do Map.put(map, key, val) end def delete(map, key) do Map.delete(map, key) end end
測試 WithoutAgent 的使用:code
iex> m = WithoutAgent.start %{} iex> WithoutAgent.get(m, "map-key") nil iex> m = WithoutAgent.put(m, "map-key", "map-val") %{"map-key" => "map-val"} iex> WithoutAgent.get(m, "map-key") "map-val" iex> m = WithoutAgent.delete(m, "map-key") %{} iex> m = WithoutAgent.get(m, "map-key") nil
從上面的使用能夠看出,爲了使用 WithoutAgent 中的狀態(一個 map),外部還必需要本身管理 WithoutAgent 的返回的狀態 m,
經過 WithoutAgent 來改變狀態時,每次都要將當前狀態 m 做爲一個參數傳給 WithoutAgent。htm
只有一個進程使用 WithoutAgent 時,上述方式沒有什麼問題,當有多個進程使用 WithoutAgent,每一個進程持有的狀態 m 很難保持一致。blog
elixir 中的 Agent 其實並非 elixir 發明的新東西,而是封裝了 erlang OTP 中現有的 ETS (Erlang Term Storage)
使用 Agent 來從新實現上面的例子:進程
defmodule WithAgent do def start do Agent.start_link(fn -> Map.new end, name: __MODULE__) end def get(key) do Agent.get(__MODULE__, fn map -> if Map.has_key?(map, key) do Map.get(map, key) else nil end end) end def put(key, val) do Agent.update(__MODULE__, &Map.put(&1, key, val)) end def delete(key) do Agent.get_and_update(__MODULE__, fn map -> if Map.has_key?(map, key) do Map.pop(map, key) else nil end end) end end
測試 WithAgent 的使用:get
iex> WithAgent.start {:ok, #PID<0.108.0>} iex> WithAgent.get("map-key") nil iex> WithAgent.put("map-key", "map-val") :ok iex> WithAgent.get("map-key") "map-val" iex> WithAgent.delete("map-key") "map-val" iex> WithAgent.get("map-key") nil
從上面的使用中能夠看出,使用了 Agent 以後,使用方徹底不用本身管理 WithAgent 的狀態,只要操做狀態便可。
這樣,多個進程同時使用 WithAgent 時,也不用管狀態衝突的事情,狀態若是有衝突也是在 WithAgent 中本身管理。it
總的來講,Agent 就是狀態的簡單的封裝,方便進程間狀態的共享。
此外,除了上面用的方法,Agent 中的全部方法參照:Agentio
來源:http://blog.iotalabs.io/table