<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                2-Agent ======== 本章我們將創建一個名為```KV.Bucket```的模塊。這個模塊負責存儲可被不同進程讀寫的鍵值對。 如果你跳過了“入門”手冊,或者是太久以前讀的,那么建議你最好重新閱讀一下關于 **進程** 的那一章。 它是本節所內容的起點。 ## 2.1-狀態的麻煩 Elixir是一種“(變量值)不可變”的語言。默認情況下,沒有什么是被共享的。 如果想要提供某種狀態,通過其創建可以從不同地方訪問的“桶”,我們有兩種選擇: * 進程 * ETS([Erlang Term Storage](http://www.erlang.org/doc/man/ets.html)) 我們之前介紹過進程,但ETS是個新東西,在后面的章節中再去探討。 而當用到進程時,我們很少會去自己動手從底層做起,而是用Elixir和OTP中抽象出來的東西代替: * [Agent](http://elixir-lang.org/docs/stable/elixir/Agent.html) - 對狀態簡單的封裝 * [GenServer](http://elixir-lang.org/docs/stable/elixir/GenServer.html) - “通用的服務器”(進程)。它封裝了狀態,提供了同步或異步調用,支持代碼熱更新等等 * [GenEvent](http://elixir-lang.org/docs/stable/elixir/GenEvent.html) - “通用的事件”管理器,允許向多個接收者發布事件消息 * [Task](http://elixir-lang.org/docs/stable/elixir/Task.html) - 計算處理的異步單元,可以派生出進程并稍后收集計算結果 我們在本“進階”手冊中會逐一討論這些抽象物。 記住它們都是在進程基礎上實現的,使用Erlang虛擬機提供的基本特性, 如```send```,```receive```,```spawn```和```link```。 ## 2.2-Agents [Agent](http://elixir-lang.org/docs/stable/elixir/Agent.html)是對狀態簡單的封裝。 如果你想要一個可以保存狀態的地方(進程),那么Agent就是不二之選。 讓我們在工程里啟動一個```iex```對話: ``` $iex -S mix ``` 然后“玩弄”一下Agent: ```elixir iex> {:ok, agent} = Agent.start_link fn -> [] end {:ok, #PID<0.57.0>} iex> Agent.update(agent, fn list -> ["eggs"|list] end) :ok iex> Agent.get(agent, fn list -> list end) ["eggs"] iex> Agent.stop(agent) :ok ``` 這里用某個初始狀態(空列表)啟動了一個agent,然后執行了一個命令來修改這個狀態, 加了一個新的列表項到頭部。```Agent.update/3```的第二個參數是一個匿名函數: 它使用agent當前狀態為輸入,返回想要的新狀態。 最終我們獲取整個列表。```Agent.get/3```函數的第二個參數是個匿名函數: 它使用當前狀態為輸入,返回的值就是```Agent.get/3```的返回值。 一旦我們用完agent,我們調用```Agent.stop/1```來終止agent進程。 現在我們用Agent來實現```KV.Bucket```。當時在開始之前,我們先寫些測試。 新建文件```test/kv/bucket_test.exs```(回想一下```.exs```文件),內容是: ```elixir defmodule KV.BucketTest do use ExUnit.Case, async: true test "stores values by key" do {:ok, bucket} = KV.Bucket.start_link assert KV.Bucket.get(bucket, "milk") == nil KV.Bucket.put(bucket, "milk", 3) assert KV.Bucket.get(bucket, "milk") == 3 end end ``` 我們的第一條測試很直白:啟動一個```KV.Bucket```,然后執行```get/2```和```put/2```操作。 最后判斷結果。我們不需要顯式地停止agent進程。 因為該test里面用到的agent進程是鏈接到測試進程的,測試進程一結束它就會跟著結束。 同時還要注意我們向```ExUnit.Case```傳遞了一個```async:true```的選項。 這個選項使得該測試用例與其它同樣包含```:async```選項的測試用例并行執行。 這種方式能夠更好地利用計算機多核的能力。但要注意,這樣的話,測試用例不能依賴或改變某些全局的值。 比如測試需要向文件系統里寫入文字,或者注冊進程,或者訪問數據庫等。 你在放置```:async```標記前必須考慮會不會在兩個測試之間造成資源競爭。 不管是不是異步執行的,很明顯我們的測試會失敗,因為該實現的功能一個都沒實現。 為了修復失敗的用例,我們來創建文件```lib/kv/bucket.ex```,輸入以下內容。 你可以不看下方的代碼,自己隨便嘗試著創建agent的行為: ```elixir defmodule KV.Bucket do @doc """ Starts a new bucket. """ def start_link do Agent.start_link(fn -> %{} end) end @doc """ Gets a value from the `bucket` by `key`. """ def get(bucket, key) do Agent.get(bucket, &Map.get(&1, key)) end @doc """ Puts the `value` for the given `key` in the `bucket`. """ def put(bucket, key, value) do Agent.update(bucket, &Map.put(&1, key, value)) end end ``` 我們使用圖(Map)來存儲我們的鍵和值。函數捕捉符號```&```在《入門》中介紹過。 現在```KV.Bucket```模塊定義好了,測試都通過了!你可以執行```mix test```試試。 ## 2.3-ExUnit回調函數 在繼續為```KV.Bucket```加入更多功能之前,先講一講ExUnit的回調函數。 你可能已經想到,每一個```KV.Bucket```的測試用例都需要用到bucket。 它要在該測試用例啟動時設置好,還要在該測試用例結束時停止。 幸運的是,ExUnit支持回調函數,使我們跳過這重復機械的任務。 讓我們使用回調機制重寫剛才的測試: ```elixir defmodule KV.BucketTest do use ExUnit.Case, async: true setup do {:ok, bucket} = KV.Bucket.start_link {:ok, bucket: bucket} end test "stores values by key", %{bucket: bucket} do assert KV.Bucket.get(bucket, "milk") == nil KV.Bucket.put(bucket, "milk", 3) assert KV.Bucket.get(bucket, "milk") == 3 end end ``` 我們首先利用```setup/1```宏,創建了設置bucket的回調函數。 這個函數會在每條測試用例執行前被執行一次,并且是與測試在同一個進程里。 注意我們需要一個機制來傳遞創建好的```bucket```的pid給測試用例。 我們使用 _測試上下文_ 來達到這個目的。 當在回調函數里返回```{:ok, bucket: bucket}```的時候, ExUnit會把該返回值元祖(字典)的第二個元素merge進測試上下文中。 測試上下文是一個圖,我們可以在測試用例的定義中匹配它,從而獲取這個上下文的值給用例中的代碼使用: ```elixir test "stores values by key", %{bucket: bucket} do # `bucket` is now the bucket from the setup block end ``` 更多信息可以參考[ExUnit.Case](http://elixir-lang.org/docs/stable/ex_unit/ExUnit.Case.html)模塊文檔, 以及[回調函數](http://elixir-lang.org/docs/stable/ex_unit/ExUnit.Callbacks.html)。 ## 2.4-其它Agent行為 除了“讀取”或者“修改”agent的狀態,agent還允許我們使用 函數```Agent.get_and_update/2```“讀取并修改”它維持的狀態。 我們用這個函數來實現刪除```KV.Bucket.delete/2```功能---從bucket中刪除一個值,并返回該值: ```elixir @doc """ Deletes `key` from `bucket`. Returns the current value of `key`, if `key` exists. """ def delete(bucket, key) do Agent.get_and_update(bucket, &Map.pop(&1, key)) end ``` 現在輪到你來給上面的代碼寫個測試啦。 你可以閱讀[Agent模塊的文檔](http://elixir-lang.org/docs/stable/elixir/Agent.html) 獲取更多信息。 ## 2.5-Agent中的C/S模式 在進入下一章之前,讓我們討論一下agent中的C/S二元模式。 先來展開剛剛寫好的```delete/2```函數: ```elixir def delete(bucket, key) do Agent.get_and_update(bucket, fn dict-> Map.pop(dict, key) end) end ``` 我們傳遞給agent的函數中的任何東西,都會出現在agent的進程里。 在這里,因為agent進程負責接收和回復我們的消息,因此可以說agent進程就是個服務器(服務端)。 而那個方法之外的任何東西,都被看成是在客戶端的范圍內。 這個區別很重要。如果有大量的工作要做,你必須考慮這個工作是放在客戶端還是在服務器上執行。比如: ```elixir def delete(bucket, key) do :timer.sleep(1000) # puts client to sleep Agent.get_and_update(bucket, fn dict -> :timer.sleep(1000) # puts server to sleep Map.pop(dict, key) end) end ``` 當服務器上執行一個很耗時的工作時,所有其它對該服務器的請求都必須等待,直到那個工作完成。 這會造成客戶端的超時。 下一章我們會探索通用服務器GenServer,它在概念上對服務器與客戶端的隔離更明顯。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看