Elixir 状态

2023-12-15 14:03 更新

目前我们还没有讲过状态.如果你的应用需要状态,比如说,来保存你的应用设置,或你需要解读一个文件并保存在内存中,你该如何存放它们?

最通常的答案是进程.我们可以编写一个无限循环的进程来保存状态,收发信息.例如,让我们来编写一个模块,内容是开启一个像键值对一样运作的进程,存放在​kv.exs​文件中:

defmodule KV do
  def start_link do
    Task.start_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

注意start_link函数开启了一个运行loop/1函数的新进程,参数是一个空映射.loop/1函数等待着信息,并且对每个信息做出合适的反应.当匹配到:get信息时,它会反馈给调用者一个信息并再次调用loop/1,等待新的信息.当:put信息以新版本的映射调用了loop/1时,给定的keyvalue就被存储了.

让我们试着运行iex kv.exs:

iex> {:ok, pid} = KV.start_link
#PID<0.62.0>
iex> send pid, {:get, :hello, self()}
{:get, :hello, #PID<0.41.0>}
iex> flush
nil
:ok

一开始,进程映射中没有键,所以发送一个:get信息,然后刷新当前进程的收件箱会得到nil.让我们发送一个:put信息并再试一次:

iex> send pid, {:put, :hello, :world}
{:put, :hello, :world}
iex> send pid, {:get, :hello, self()}
{:get, :hello, #PID<0.41.0>}
iex> flush
:world
:ok

注意进程是如何保存状态的,以及我们可以通过向进程发送信息来获取和更新这个状态.事实上我们可以向任何已知pid的进程发送信息并操作状态.

也可以用一个名字注册pid,并允许任何知道这个名字的人发送信息给它:

iex> Process.register(pid, :kv)
true
iex> send :kv, {:get, :hello, self()}
{:get, :hello, #PID<0.41.0>}
iex> flush
:world
:ok

使用进程存放状态,以及名字注册在Elixir中都是非常普遍的模式.然而,通常我们不会像上面那样手工操作这些模式,而是使用Elixir装载的一些抽象工具.例如,Elixir提供了代理,它是状态的一种简单抽象:

iex> {:ok, pid} = Agent.start_link(fn -> %{} end)
{:ok, #PID<0.72.0>}
iex> Agent.update(pid, fn map -> Map.put(map, :hello, :world) end)
:ok
iex> Agent.get(pid, fn map -> Map.get(map, :hello) end)
:world

Agent.start_link/2可以设置:name选项,并且会自动注册.除了代理,Elixir还提供了用于创建通用服务器(GenServer),任务等等的API,它们的底层都是由进程支持的.我们将在Mix和OTP入门中沿着管理者树仔细探索,同时从头到尾创建一个完整的Elixir应用.


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号