Elixir 阻塞了 GenServer 进程
Elixir blocked GenServer process
我有一个简单的 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...")
我从未在上述调用中看到 return。我想这很明显。所以我的问题是,如何在不阻止下游执行任务的过程的情况下创建我想要的循环?
谢谢。
由于您是在 init 函数中调用循环,因此循环会无限阻塞并且 init/1 回调永远不会 returns.
对 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
最好 运行 另一个处理计时器循环的进程,并让它在应该执行操作时向您的 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
best/cleanest 完成您正在尝试做的事情的方法是 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 就像一个事件循环,那么为什么要自己重新实现呢?
我有一个简单的 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...")
我从未在上述调用中看到 return。我想这很明显。所以我的问题是,如何在不阻止下游执行任务的过程的情况下创建我想要的循环?
谢谢。
由于您是在 init 函数中调用循环,因此循环会无限阻塞并且 init/1 回调永远不会 returns.
对 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
最好 运行 另一个处理计时器循环的进程,并让它在应该执行操作时向您的 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
best/cleanest 完成您正在尝试做的事情的方法是 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 就像一个事件循环,那么为什么要自己重新实现呢?