前面我們已經看過Erlang的Concurrency一些實例.
今天來講一下原理.
Erlang的process會在名稱為BEAM的VM上面執行,如下圖所示:
BEAM deafult會產生數個scheduler,對應系統CPU,例如使用4核心的機器,
就會產生4個scheduler.
裡面執行的Erlang process,與系統的process不同.是很輕量的,
使用的記憶體也很少,基本使用1-2KB.而且透過此種架構,可以有
效發揮CPU的運算能力,我們無須寫複雜的程式碼,細節都由Erlang
幫我們處理好了.各個process是獨立的,因為記憶體不共享,變數
不能被改變,若某一個process崩潰,就被隔離,不會影響到整個系統.
在Erlang的世界,百萬個process在跑,是稀鬆平常的事.
Elixir身為Erlang的 meta programming language,一樣可以使用
如此強大的機制.其實Elixir的程式,編譯後就是Erlang的beam檔.
接著我們來看看Elixir的process吧.
在Elixir中建立process,一樣是使用spawn.
為了說明方便起見,先建立一個function,會sleep兩秒,
模擬執行query.
iex(1)> run_query = fn(query_def) ->
...(1)> :timer.sleep(2000)
...(1)> "#{query_def} result"
...(1)> end
#Function<6.90072148/1 in :erl_eval.expr/5>
iex(2)> async_query = fn(query_def) ->
...(2)> spawn(fn -> IO.puts(run_query.(query_def)) end)
...(2)> end
#Function<6.90072148/1 in :erl_eval.expr/5>
iex(3)> async_query.("query 1")
#PID<0.63.0>
query 1 result
接著我們學一下IP Man(葉問),打十個好了.
iex(4)> Enum.each(1..10, &(async_query.("query #{&1}")))
:ok
query 2 result
query 1 result
query 9 result
query 8 result
query 7 result
query 6 result
query 5 result
query 4 result
query 3 result
query 10 result
訊息傳送使用 send/2, 接收方使用
receive do
pattern_1 -> do_something
pattern_2 -> do_anotherthing
end
這樣的方式來接收.
來看範例:
defmodule Spawn1 do
def greet do
receive do
{sender, msg} ->
send(sender, {:ok, "Hello #{msg}"})
end
end
end
# client
pid = spawn(Spawn1, :greet, [])
send(pid, {self, "Miku!"})
receive do
{:ok, message} ->
IO.puts message
end
執行結果:
iex(5)> c "spawn1.ex"
Hello Miku!
[Spawn1]
兩邊互相傳送訊息.有沒有注意到 IO.puts 把()省略了.
在Exixir 引用函數時,後面的() 可以省略.
上面程式可以改成:
defmodule Spawn1 do
def greet do
receive do
{sender, msg} ->
send sender, {:ok, "Hello #{msg}"}
end
end
end
# client
pid = spawn(Spawn1, :greet, [])
send pid, {self, "Miku!"}
receive do
{:ok, message} ->
IO.puts message
end
這樣可以避免函數的()過多,讓我們專注於整個流程與邏輯.
Elixir一樣具有 timeout,也是使用 after;
一樣可以把process註冊名稱.
這些部份,在前面Erlang的部份,都已經介紹過了,在此不重複.