2015-12-09 80 views
1

我有一个简单GenServer内,我希望创建一个循环调用一个函数每两秒钟:药剂封锁GenServer过程

defmodule MyModule do 
    use GenServer 

    def start_link(time) do 
    GenServer.start_link(__MODULE__,time) 
    end 

    #Start loop 
    def init(time) do 
    {:ok, myLoop(time)} 
    end 

    #Loop every two seconds 
    def myLoop(time) do 

    foo = bah(:someOtherProcess, {time}) 
    IO.puts("The function value was: #{foo}") 
    :timer.sleep(2000) 
    myLoop(time + 2) 
    end 
end 

但是,当我打电话有:

{:ok, myServer} =MyModule.start_link(time) 
IO.puts("Now I can carry on...") 

我从来没有看到上面的电话回复。我猜想这很明显。所以我的问题是,我怎么能创建我想要的循环,而不会阻止下游执行任务的进程?

谢谢。

回答

7

最好/最干净的方式来完成你想要做的是Process.send_after/3。它将超时授权给调度程序,而不是另一个进程。

defmodule MyModule do 
    use GenServer 

    def start_link(time) do 
    GenServer.start_link(__MODULE__,time) 
    end 

    def init(time) do 
    schedule_do_something(time) 
    {:ok, %{time: time}} 
    end 

    def handle_info(:do_something, state) do 
    %{time: time} = state 
    # do something interesting here 
    schedule_do_something(time) 
    {:noreply, state} 
    end 

    defp schedule_do_something(time_in_seconds) do 
    Process.send_after(self, :do_something, (time_in_seconds * 1000)) 
    end 
end 

无论如何,GenServer的行为就像一个事件循环,所以为什么要自己重新实现呢?

2

由于您在init函数中调用您的循环,因此您的循环无限制地阻止,并且init/1回调永不返回。

对init执行操作的常用技术是向GenServer发送消息并使用handle_info/2执行操作。当你这样做时,你应该记得包括一个捕获全部为handle_info

defmodule MyModule do 
    use GenServer 

    def start_link(time) do 
    {:ok, pid} = GenServer.start_link(__MODULE__,time) 
    send(pid, {:start_loop, time}) 
    {:ok, pid} 
    end 

    #Start loop 
    def init(time) do 
    {:ok, nil} 
    end 

    def handle_info({:start_loop, time}, state) do 
    myLoop(time) 
    {:noreply, state} 
    end 

    #catch all 
    def handle_info(_, state) do 
    {:noreply, state} 
    end  

    #Loop every two seconds 
    def myLoop(time) do 
    IO.puts("The function value was: #{time}") 
    :timer.sleep(2000) 
    myLoop(time + 2) 
    end 
end 
0

最好是运行另一个进程来处理计时器循环,并在执行该操作时向genserver发送消息。这样GenEvent进程大多处于空闲状态以处理其他消息,但是它会在应该采取定期行动时通知它。

您可以通过在spawn_link的自己的流程中运行您的MyModule.myloop函数来完成此操作。这方面的例子是以下:

defmodule MyModule do 
    use GenServer 

    def start_link(time) do 
    GenServer.start_link(__MODULE__,time) 
    end 

    #Start loop 
    def init(time) do 
    spawn_link(&(MyModule.myloop(0, self)) 
    {:ok, nil} 
    end 

    def handle_info({:timer, time}, state) do 
    foo = bah(:someOtherProcess, {time}) 
    IO.puts("The function value was: #{foo}") 
    {:noreply, state} 
    end 
    def handle_info(_, state) do 
    {:noreply, state} 
    end 

    #Loop every two seconds 
    defp myLoop(time, parent) do 
    send(parent, {:timer, time}) 
    :timer.sleep(2000) 
    myLoop(time + 2) 
    end 
end 

如果你不这么费心有关路过的时候作为消息的一部分(你可以基于该GenServer状态或类似计算的话),你可以使用Erlang的功能:timer.send_interval每个周期都会发送一条消息,类似于上面的myLoop函数。文档在这里:http://www.erlang.org/doc/man/timer.html#send_interval-2

+2

有一个内置函数':timer.send_interval/2',可以完成你所需要的功能,而无需自己编写代码。 –