使用spawn創建一個新進程,其第一個參數是模塊名、第二個參數是函數名、第三個參數是參數列表。spawn會返回一個進程標識符,通常叫做PID。
defmodule Spawn1 dodef greet doreceive do{sender, msg} ->send sender, { :ok, "Hello #{msg}" }# codeendend endspawn(SpawnBasic, :greet, []) #"Hello"
進程間發送消息
使用send發送消息,第一個參數是接收方pid、第二個參數是要發送的消息,通常是原子或者元組。使用receive等待消息,它的用法比較像case。
#以下代碼都在同一個文件中。
defmodule Spawn1 dodef greet doreceive do{sender, msg} ->send sender, { :ok, "Hello #{msg}" }# codeendend endpid = spawn(Spawn1, :greet, []) send pid, {self, "World!"}receive do{:ok, message} ->IO.puts message end
上述代碼如果想要發送第二條數據,就會導致iex被掛起。因為greet函數在處理完receive后,就退出了。我們永遠等不來receive的相應。我們可以使用 after 指定receive在規定時間內未到達就超時。
receive do{:ok, message} ->IO.puts messageafter 500 -> #receive在等待500毫秒后,消息未到達就會退出IO.puts "The greeter han gone away" end
?
我們可以使用遞歸來處理多條消息。greet:
def greet doreceive do{sender, msg} ->send, sender, { :ok, "Hello, #{msg}" }greetendend endpid = spawn(Spawn1, :greet, []) send pid, {self, "World!"}receive do{:ok, message} ->IO.puts message # "Hello, World!" endpid = spawn(Spawn1, :greet, []) send pid, {self, "kermit!"}receive do{:ok, message} ->IO.puts message # "Hello, Kermit!"after 500 ->IO.puts "The greeter has gone away" end
?
進程開銷
很小。
?
進程調用exit(:boom)會以狀態碼99退出。
?
關聯兩個進程,使用spawn_link會創建一個進程并和調用者相關聯。
defmodule Link2 do import :timer, only: [ sleep: 1 ]def sad_function dosleep 500exit(:boom)enddef run dospawn_link(Link2, :sad_function, [])receive domsg ->IO.puts "<MESSAGE RECEIVED: #{inspect msg}"after 1000 ->IO.puts "Nothing ... "endend end
子進程結束,然后它會結束整個應用,這是關聯進程的默認行為,當其中一個進程非正常退出時會把另一個進程也殺死。
設置Peocess.flag(:trap_exit, true),可以捕獲進程退出時的消息。
創建進程時,可以使用spawn_monitor開啟監控(創建 + 監控),也可以使用Process.monitor監控已經存在的進程。當使用Process.monitor時,在調用監控完成前被監控進程死了,就不會受到通知。然而,spawn_link和spawn_monitor操作符合原子性,所以總能捕獲到錯誤。?
?
并行map
普通map返回列表,該列表是某個收集的每個元素應用于某個函數的結果。并行版本做同樣的事情,但每個元素在獨立的進程里應用于函數。
defmodule Parallel dodef pmap(collection, fun) dome = selfcollection|> Enum.map(fn (elem) -> #雙重嵌套函數spawn_link fn -> (send me, { self, fun.(elem) } ) end #返回一組PID,每個PID內都運行了一個計算函數,將自身PID和結果發送給主進程end)|> Enum.map(fn (pid) -> #在主進程中等待結果receive do {^pid, resuet } -> result endend)end end
?
斐波拉契數服務器
編寫一個服務程序來計算斐波拉契數。當計算器準備好接受下一個數字時,它會發送 :ready 消息給調度器,如果仍有任務為完成,調度器會發送 :fib 消息給計算器;否則發送shutdown 給計算器。當計算器接受到 :fib 消息就計算給定的斐波拉契數,并以 :answer 返回結果。如果收到shutdown就退出。
defmodule FibSolver do #計算模塊def fib(scheduler) dosend scheduler, {:ready, self}receive do{ :fib, n, client} ->send client, { :answer, n, fib_calc(n), self}fib(scheduler){ :shutdown } ->exit(:normal)endenddefp fib_calc(0) do0enddefp fib_calc(1) do1enddefp fib_calc(n) dofib_calc(n - 1) + fib_calc(n - 2)end enddefmodule Scheduler do #調度模塊def run(num_process, module, func, to_calculate) do(1..num_process)|> Enum.map(fn (_) -> spawn(module, func, [self]) end)|> scheduler_process(to_calculate, [])enddefp scheduler_process(processes, queue, results) doreceive do{:ready, pid} when length(queue) > 0 ->[next | tail] = queuesend pid, {:fib, next, self}scheduler_process(processes, tail, results){:ready, pid} ->send pid, {:shutdown}if length(processes) > 1 doscheduler_process(List.delete(processes, pid), queue, results)elseEnum.sort(results, fn {n1, _}, {n2, _} -> n1 <= n2 end)end{:answer, number, result, _pid}->scheduler_process(processes, queue, [ {number,result} | results])endend endto_process = [ 37, 37, 37, 37, 37, 37] #外部調用模塊Enum.each 1..10, fn num_processes ->{time, result} = :timer.tc(Scheduler, :run, [num_processes, FibSolver, :fib, to_process])if num_processes == 1 doIO.puts inspect resultIO.puts "\n # time (s)"end:io.format "~2B ~.2f~n", [num_processes, time/1000000.0] end
?