Elixir 狀態(tài)

2023-12-15 14:03 更新

目前我們還沒有講過狀態(tài).如果你的應(yīng)用需要狀態(tài),比如說,來保存你的應(yīng)用設(shè)置,或你需要解讀一個文件并保存在內(nèi)存中,你該如何存放它們?

最通常的答案是進(jìn)程.我們可以編寫一個無限循環(huán)的進(jìn)程來保存狀態(tài),收發(fā)信息.例如,讓我們來編寫一個模塊,內(nèi)容是開啟一個像鍵值對一樣運作的進(jìn)程,存放在?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函數(shù)開啟了一個運行loop/1函數(shù)的新進(jìn)程,參數(shù)是一個空映射.loop/1函數(shù)等待著信息,并且對每個信息做出合適的反應(yīng).當(dāng)匹配到:get信息時,它會反饋給調(diào)用者一個信息并再次調(diào)用loop/1,等待新的信息.當(dāng):put信息以新版本的映射調(diào)用了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

一開始,進(jìn)程映射中沒有鍵,所以發(fā)送一個:get信息,然后刷新當(dāng)前進(jìn)程的收件箱會得到nil.讓我們發(fā)送一個: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

注意進(jìn)程是如何保存狀態(tài)的,以及我們可以通過向進(jìn)程發(fā)送信息來獲取和更新這個狀態(tài).事實上我們可以向任何已知pid的進(jìn)程發(fā)送信息并操作狀態(tài).

也可以用一個名字注冊pid,并允許任何知道這個名字的人發(fā)送信息給它:

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

使用進(jìn)程存放狀態(tài),以及名字注冊在Elixir中都是非常普遍的模式.然而,通常我們不會像上面那樣手工操作這些模式,而是使用Elixir裝載的一些抽象工具.例如,Elixir提供了代理,它是狀態(tài)的一種簡單抽象:

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可以設(shè)置:name選項,并且會自動注冊.除了代理,Elixir還提供了用于創(chuàng)建通用服務(wù)器(GenServer),任務(wù)等等的API,它們的底層都是由進(jìn)程支持的.我們將在Mix和OTP入門中沿著管理者樹仔細(xì)探索,同時從頭到尾創(chuàng)建一個完整的Elixir應(yīng)用.


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號