elixir 本身是一种 immutable 的语言,默认情况下,进程间是不共享任何状态的,进程之间通过消息来交互。
而 agent 则封装了一种进程间共享状态的方式,通过这种方式,不用显式的写 send/receieve 的代码,就能方便的在进程之间共享状态。
首先,看一个在不用 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 的使用:
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。
只有一个进程使用 withoutagent 时,上述方式没有什么问题,当有多个进程使用 withoutagent,每个进程持有的状态 m 很难保持一致。
使用 agent 来管理状态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 的使用:
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 中自己管理。
总的来说,agent 就是状态的简单的封装,方便进程间状态的共享。
此外,除了上面用的方法,agent 中的所有方法参照:agent
来源:http://blog.iotalabs.io/