zoukankan      html  css  js  c++  java
  • 多进程

      使用spawn创建一个新进程,其第一个参数是模块名、第二个参数是函数名、第三个参数是参数列表。spawn会返回一个进程标识符,通常叫做PID。

    defmodule Spawn1 do
        def greet do
            receive do
                {sender, msg} ->
                    send sender, { :ok, "Hello #{msg}" }
                # code
            end
            
        end
    end
    
    spawn(SpawnBasic, :greet, [])        #"Hello"

     

    进程间发送消息

      使用send发送消息,第一个参数是接收方pid、第二个参数是要发送的消息,通常是原子或者元组。使用receive等待消息,它的用法比较像case。

    #以下代码都在同一个文件中。
    defmodule Spawn1 do def greet do
    receive do {sender, msg} -> send sender, { :ok, "Hello #{msg}" } # code end end end pid = 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 message
        after 500 ->    #receive在等待500毫秒后,消息未到达就会退出
            IO.puts "The greeter han gone away"
    end

      我们可以使用递归来处理多条消息。greet:

    def greet do
        receive do
            {sender, msg} ->
                send, sender, { :ok, "Hello, #{msg}" }
                greet
            end
        end
    end
    
    pid = spawn(Spawn1, :greet, [])
    send pid, {self, "World!"}
    
    receive do
        {:ok, message} ->
            IO.puts message            # "Hello, World!"
    end
    
    pid = 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 do
            sleep 500
            exit(:boom)
        end
        def run do
            spawn_link(Link2, :sad_function, [])
            receive do
                msg ->
                    IO.puts "<MESSAGE RECEIVED: #{inspect msg}"
            after 1000 ->
                IO.puts "Nothing ... "
            end
        end
    end

      子进程结束,然后它会结束整个应用,这是关联进程的默认行为,当其中一个进程非正常退出时会把另一个进程也杀死。

      设置Peocess.flag(:trap_exit, true),可以捕获进程退出时的消息。

      创建进程时,可以使用spawn_monitor开启监控(创建 + 监控),也可以使用Process.monitor监控已经存在的进程。当使用Process.monitor时,在调用监控完成前被监控进程死了,就不会受到通知。然而,spawn_link和spawn_monitor操作符合原子性,所以总能捕获到错误。 

    并行map

      普通map返回列表,该列表是某个收集的每个元素应用于某个函数的结果。并行版本做同样的事情,但每个元素在独立的进程里应用于函数。

    defmodule Parallel do
        def pmap(collection, fun) do
            me = self
            collection
            |> 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 end
            end)
        end
    end

    斐波拉契数服务器

      编写一个服务程序来计算斐波拉契数。当计算器准备好接受下一个数字时,它会发送 :ready 消息给调度器,如果仍有任务为完成,调度器会发送 :fib 消息给计算器;否则发送shutdown 给计算器。当计算器接受到 :fib 消息就计算给定的斐波拉契数,并以 :answer 返回结果。如果收到shutdown就退出。

      

    defmodule FibSolver do    #计算模块
        def fib(scheduler) do
            send scheduler, {:ready, self}
            receive do
                { :fib, n, client} ->
                    send client, { :answer, n, fib_calc(n), self}
                    fib(scheduler)
    
                { :shutdown } ->
                    exit(:normal)
            end
            
        end
    
        defp fib_calc(0) do
            0
        end
    
        defp fib_calc(1) do
            1
        end
    
        defp fib_calc(n) do
            fib_calc(n - 1) + fib_calc(n - 2)
        end
    end
    
    
    defmodule 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, [])
        end
    
        defp scheduler_process(processes, queue, results) do
            receive do
                {:ready, pid} when length(queue) > 0 ->
                    [next | tail] = queue
                    send pid, {:fib, next, self}
                    scheduler_process(processes, tail, results)
    
                {:ready, pid} ->
                    send pid, {:shutdown}
                    if length(processes) > 1 do
                        scheduler_process(List.delete(processes, pid), queue, results)
                    else
                        Enum.sort(results, fn {n1, _}, {n2, _} -> n1 <= n2 end)
                    end
    
                {:answer, number, result, _pid}->
                    scheduler_process(processes, queue, [ {number,result} | results])
            end
            
        end
    end
    
    
    to_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 do
            IO.puts inspect result
            IO.puts "
     #    time (s)"
        end
    
        :io.format "~2B     ~.2f~n", [num_processes, time/1000000.0]
    end
  • 相关阅读:
    AMR转换MP3 linuxCentOS 版(不管任何语言可以使用shell命令在linux执行转换语句)
    MySql 入门——日期计算
    javascript深度剖析之 【 var 关键字】。
    javascript动画浅析。
    javascript之this关键字浅析。
    javascript【AMD模块加载器】浅析V3(添加CSS加载功能,重构内部流程)
    html5 canvas 自制小游戏系列之 【贪吃蛇】。
    javascript设计模式简单介绍之【工厂模式】
    javascript【AMD模块加载器】浅析
    javascript【AMD模块加载器】浅析V2(整合DOM ready)
  • 原文地址:https://www.cnblogs.com/lr1402585172/p/11507966.html
Copyright © 2011-2022 走看看