<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>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                11-進程 ======= Elixir中,所有代碼都在進程中執行。進程彼此獨立,一個接一個并發執行,彼此通過消息傳遞來溝通。 進程不僅僅是Elixir中并發的基礎,也是Elixir創建分布式、高容錯程序的本質。 Elixir的進程和操作系統中的進程不可混為一談。 Elixir的進程,在CPU和內存使用上,是極度輕量級的(不同于其它語言中的線程)。 因此,同時運行著數十萬、百萬個進程也并不是罕見的事。 本章將講解如何派生新進程,以及在進程間如何發送和接受消息等基本知識。 ## 11.1-進程派生 派生(spawning)一個新進程的方法是使用自動導入(kernel函數)的```spawn/1```函數: ```elixir iex> spawn fn -> 1 + 2 end #PID<0.43.0> ``` 函數```spawn/1```接收一個_函數_作為參數,在其派生出的進程中執行這個函數。 注意spawn/1返回一個PID(進程標識)。在這個時候,這個派生的進程很可能已經結束。 派生的進程執行完函數后便會結束: ```elixir iex> pid = spawn fn -> 1 + 2 end #PID<0.44.0> iex> Process.alive?(pid) false ``` >你可能會得到與例子中不一樣的PID 用```self/0```函數獲取當前進程的PID: ```elixir iex> self() #PID<0.41.0> iex> Process.alive?(self()) true ``` >注:上文調用```self/0```加了括號。 但是如前文所說,在不引起誤解的情況下,可以省略括號而只寫```self``` 可以發送和接收消息,讓進程變得越來越有趣。 ## 11.2-發送和接收 使用```send/2```函數發送消息,用```receive/1```接收消息: ```elixir iex> send self(), {:hello, "world"} {:hello, "world"} iex> receive do ...> {:hello, msg} -> msg ...> {:world, msg} -> "won't match" ...> end "world" ``` 當有消息被發給某進程,該消息就被存儲在該進程的郵箱里。 語句塊```receive/1```檢查當前進程的郵箱,尋找匹配給定模式的消息。 其中函數```receive/1```支持分支子句,如```case/2```。 當然也可以給子句加上衛兵表達式。 如果找不到匹配的消息,當前進程將一直等待,知道下一條信息到達。但是可以設置一個超時時間: ```elixir iex> receive do ...> {:hello, msg} -> msg ...> after ...> 1_000 -> "nothing after 1s" ...> end "nothing after 1s" ``` 超時時間設為0表示你知道當前郵箱內肯定有郵件存在,很自信,因此設了這個極短的超時時間。 把以上概念綜合起來,演示進程間發送消息: ```elixir iex> parent = self() #PID<0.41.0> iex> spawn fn -> send(parent, {:hello, self()}) end #PID<0.48.0> iex> receive do ...> {:hello, pid} -> "Got hello from #{inspect pid}" ...> end "Got hello from #PID<0.48.0>" ``` 在shell中執行程序時,輔助函數```flush/0```很有用。它清空緩沖區,打印進程郵箱中的所有消息: ```elixir iex> send self(), :hello :hello iex> flush() :hello :ok ``` ## 11.3-鏈接 Elixir中最常用的進程派生方式是通過函數```spawn_link/1```。 在舉例子講解```spawn_link/1```之前,來看看如果一個進程失敗了會發生什么: ```elixir iex> spawn fn -> raise "oops" end #PID<0.58.0> ``` 。。。啥也沒發生。這時因為進程都是互不干擾的。如果我們希望一個進程中發生失敗可以被另一個進程知道,我們需要鏈接它們。 使用```spawn_link/1```函數,例子: ```elixir iex> spawn_link fn -> raise "oops" end #PID<0.60.0> ** (EXIT from #PID<0.41.0>) an exception was raised: ** (RuntimeError) oops :erlang.apply/2 ``` 當失敗發生在shell中,shell會自動終止執行,并顯示失敗信息。這導致我們沒法看清背后過程。 要弄明白鏈接的進程在失敗時發生了什么,我們在一個腳本文件使用```spawn_link/1```并且執行和觀察它: ```elixir # spawn.exs spawn_link fn -> raise "oops" end receive do :hello -> "let's wait until the process fails" end ``` 這次,該進程在失敗時把它的父進程也弄停止了,因為它們是鏈接的。<br/> 手動鏈接進程:```Process.link/1```。 建議可以多看看[Process模塊](http://elixir-lang.org/docs/stable/elixir/Process.html),里面包含很多常用的進程操作函數。 進程和鏈接在創建能高容錯系統時扮演重要角色。在Elixir程序中,我們經常把進程鏈接到某“管理者”上。 由這個角色負責檢測失敗進程,并且創建新進程取代之。因為進程間獨立,默認情況下不共享任何東西。 而且當一個進程失敗了,也不會影響其它進程。 因此這種形式(進程鏈接到“管理者”角色)是唯一的實現方法。 其它語言通常需要我們來try-catch異常,而在Elixir中我們對此無所謂,放手任進程掛掉。 因為我們希望“管理者”會以更合適的方式重啟系統。 “要死你就快一點”是Elixir軟件開發的通用哲學。 在講下一章之前,讓我們來看一個Elixir中常見的創建進程的情形。 ## 11.4-狀態 目前為止我們還沒有怎么談到狀態。但是,只要你創建程序,就需要狀態。 例如,保存程序的配置信息,或者分析一個文件先把它保存在內存里。 你怎么存儲狀態? 進程就是(最常見的)答案。我們可以寫無限循環的進程,保存一個狀態,然后通過收發信息來告知或改變該狀態。 例如,寫一個模塊文件,用來創建一個提供k-v倉儲服務的進程: ```elixir defmodule KV do def start do {:ok, spawn_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```函數簡單地派生一個新進程,這個進程以一個空的圖為參數,執行```loop/1```函數。 這個```loop/1```函數等待消息,并且針對每個消息執行合適的操作。 加入受到一個```:get```消息,它把消息發回給調用者,然后再次調用自身```loop/1```,等待新消息。 當受到```:put```消息,它便用一個新版本的圖變量(里面的k-v更新了)再次調用自身。 執行一下試試: ```elixir iex> {:ok, pid} = KV.start #PID<0.62.0> iex> send pid, {:get, :hello, self()} {:get, :hello, #PID<0.41.0>} iex> flush nil ``` 一開始進程內的圖變量是沒有鍵值的,所以發送一個```:get```消息并且刷新當前進程的收件箱,返回nil。 下面再試試發送一個```:put```消息: ```elixir iex> send pid, {:put, :hello, :world} #PID<0.62.0> iex> send pid, {:get, :hello, self()} {:get, :hello, #PID<0.41.0>} iex> flush :world ``` 注意進程是怎么保持一個狀態的:我們通過同該進程收發消息來獲取和更新這個狀態。 事實上,任何進程只要知道該進程的PID,都能讀取和修改狀態。 還可以注冊這個PID,給它一個名稱。這使得人人都知道它的名字,并通過名字來向它發送消息: ```elixir iex> Process.register(pid, :kv) true iex> send :kv, {:get, :hello, self()} {:get, :hello, #PID<0.41.0>} iex> flush :world ``` 使用進程維護狀態,以及注冊進程都是Elixir程序非常常用的方式。 但是大多數時間我們不會自己實現,而是使用Elixir提供的抽象實現。 例如,Elixir提供的[agent](http://elixir-lang.org/docs/stable/elixir/Agent.html)就是一種維護狀態的簡單的抽象實現: ```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/2```方法加一個一個```:name```選項可以自動為其注冊一個名字。 除了agents,Elixir還提供了創建通用服務器(generic servers,稱作GenServer)、 通用時間管理器以及事件處理器(又稱GenEvent)的API。 這些,連同“管理者”樹,都可以在Mix和OTP手冊里找到詳細說明。
                  <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>

                              哎呀哎呀视频在线观看