在Gossip GenServer中,进程在退出条件之前死亡

时间:2018-10-01 07:42:45

标签: elixir gen-server

我正在通过相互发送消息来创建多个GenServers闲聊。我设置了退出条件,以使每个进程在收到10条消息后都会死亡。每个GenServer都是在launch函数的八卦开头创建的。

defmodule Gossip do
    use GenServer

    # starting gossip
    def start_link(watcher \\ nil), do: GenServer.start_link(__MODULE__, watcher)
    def init(watcher), do: {:ok, {[],0,watcher}}
    def launch(n, watcher \\ nil) do
        crew = (for _ <- 0..n, do: elem(Gossip.start_link(watcher),1))
        Enum.map(crew, &(add_crew(&1,crew--[&1])))
        crew
            |> hd()
            |> Gossip.send_msg()
    end 


    # client side
    def add_crew(pid, crew), do: GenServer.cast(pid, {:add_crew, crew})
    def rcv_msg(pid, msg \\ ""), do: GenServer.cast(pid, {:rcv_msg, msg})
    def send_msg(pid, msg \\ ""), do: GenServer.cast(pid, {:send_msg, msg})


    # server side  
    def handle_cast({:add_crew, crew}, {_, msg_counter, watcher}), do:
        {:noreply, {crew, msg_counter, watcher}}

    def handle_cast({:rcv_msg, _msg}, {crew, msg_counter, watcher}) do
        if msg_counter < 10 do
            send_msg(self())
        else
            GossipWatcher.increase(watcher)
            IO.inspect(self(), label: "exit of:") |> Process.exit(:normal)
        end
        {:noreply, {crew, msg_counter+1, watcher}}
    end

    def handle_cast({:send_msg,_},{[],_,_}), do: Process.exit(self(),"crew empty")
    def handle_cast({:send_msg, _msg}, {crew, msg_counter, watcher}=state) do
        rcpt = Enum.random(crew) ## recipient of the msg
        if Process.alive?(rcpt) do
            IO.inspect({self(),rcpt}, label: "send message from/to")
            rcv_msg(rcpt, "ChitChat")
            send_msg(self())
            {:noreply, state}
        else
        IO.inspect(rcpt, label: "recipient is dead:")
        {:noreply, {crew -- [rcpt], msg_counter, watcher}}
        end
    end
end


defmodule GossipWatcher do
    use GenServer

    def start_link(opt \\ []), do: GenServer.start_link(__MODULE__, opt)
    def init(opt), do: {:ok, {0}}
    def increase(pid), do: GenServer.cast(pid, {:increase})  
    def handle_cast({:increase}, {counter}), do:
        IO.inspect({:noreply, {counter+1}}, label: "toll of dead")

end

在收到10条消息后,我使用模块GossipWatcher监视死亡的GenServer数量。问题是 iex会提示回来,而还有一些GenServers仍然存在。例如,超过1000 GenServer的人在闲话的末尾只有约964 GenServers人死亡。

iex(15)> {:ok, watcher} = GossipWatcher.start_link
{:ok, #PID<0.11163.0>}
iex(16)> Gossip.launch 100, watcher            
send message from/to: {#PID<0.11165.0>, #PID<0.11246.0>}
:ok     
send message from/to: {#PID<0.11165.0>, #PID<0.11167.0>}
send message from/to: {#PID<0.11246.0>, #PID<0.11182.0>}
send message from/to: {#PID<0.11165.0>, #PID<0.11217.0>}
...
toll of dead: {:noreply, {960}}
toll of dead: {:noreply, {961}}
toll of dead: {:noreply, {962}}
toll of dead: {:noreply, {963}}
toll of dead: {:noreply, {964}}
iex(17)>

我在这里想念什么吗?流程是否超时?任何帮助将不胜感激
TIA。

1 个答案:

答案 0 :(得分:1)

您的代码中可以起到一些技巧的部分在这里:

def handle_cast({:send_periodic_message}, zero_counter_gossip_true) do

    ...

    if (Process.alive?(rcpt)) == true do

    ...

    else
        IO.inspect(rcpt, label: "recipient is dead:")
        {:noreply, {crew -- [rcpt], msg_counter, watcher}}
    end
end

在其他情况下,您允许GenServer 停止工作:由于它不会向邻居或他自己发送消息,因此不会启动“操作”,只是停止做某事。
在最坏且不太可能的情况下:如果您启动2000 GenServer并从一个GenServer中启动八卦,并且第一个仅与第二个对话,那么第二个也仅与第一个对话。 ..然后只有一个GenServer会死掉,您会返回命令提示符,仍然有1999 GenServer活着,但什么也不做(因为它们收到0条消息)。

即使这种情况牵强附会,它也表明八卦的执行可以在每个GenServer收到10条消息之前提前结束。因此,您描述的行为。


我做了一些测试,rewriting your code,并使用第二种类型的GenServer来监视有多少GenServers被杀死以及有多少幸存者。事实证明,在返回GenServers提示后,在1000 GenServer中,平均还有40 iex仍然活着。

相关问题