2016-02-12 27 views
5

我知道我可以调用一个GenServer这样如何在GenServer中执行对当前进程的调用?

GenServer.call(pid, request) 
# using a pid 

或类似这样的

GenServer.call(registered_name, request) 
# if I registered the process with a name 

但是,有没有办法EXCUTE模块内部的GenServer.call不知道PID /注册名称?(即有没有像GenServer.call(__CURRENT_PROCESS__, request)?)

回答

4

我不确定这是最好的方式去,但你要找的是Kernel.self/0

编辑:

每萨沙的评论的代码是这样的:

GenServer.call(self, request) 

编辑:

每通过帕维尔Obrok所作的出色一点,你不应该做的当前进程的调用。如果您需要发送消息给当前的进程,你应该做这个FASION:

GenServer.cast(self, request) 

NB它的铸造与调用。

+0

就像Onorio所说,你可以使用GenServer.call(self,request)。看看: http://elixir-lang.org/getting-started/processes.html 或小药剂和OTP指南,如果你想深入了解进程和OTP平台。 –

+0

@Onorio为什么它不是一个好主意? – user3790827

+0

@ user3790827我没有说这不是一个好主意。我说我不确定这是最好的方式。由于你的问题几乎没有背景,所以很难说。 –

5

这将不起作用。一个GenServer.call只是发送一个消息给给定的进程,然后等待另一个消息(回复),阻止当前进程。如果您以这种方式向self发送消息,则该进程将不会自由处理该消息,因为它将被阻止等待答复。所以call将永远超时。

我怀疑你需要的仅仅是将你想要的功能提取到一个函数中,然后直接调用它。或者,您可以发送cast到当前流程,有效地告诉它“稍后”执行某些操作。

+0

Obrok我正在使用该调用来检索数据。我记得我已经在某处读到过,当你将数据发送到一个进程而不需要响应时使用'cast',而当你真的想从另一个进程中检索数据时''调用''。 – user3790827

+0

Obrok我已经在Elixir入门指南[这里]看过它(http://elixir-lang.org/getting-started/mix-otp/genserver.html#call-cast-or-info) – user3790827

+0

' call'确实用于检索数据,但不是从当前进程中获取数据。如果你在一个'GenServer'中,你可能处于'handle_call','handle_cast'或'handle_info'回调 - 你可以访问'GenServer'的状态,所以不应该有原因还需要从同一过程中请求它。 –

1

从我所了解的您的评论中,您试图为您的GenServer编写一个公共API函数,而不是在GenServer流程中编写call。 AFAIK在没有知道PID的情况下没有办法做到这一点。但是,对于其中的GenServers,您只能创建一个实例,因此存在这种情况的习惯用法:使用实现该实例的模块的名称命名GenServer的唯一实例。这可以很容易地使用要做的__MODULE__宏:

def start_link do 
    GenServer.start_link(__MODULE__, nil, name: __MODULE__) 
end 

然后在你的API函数,你只需要使用__MODULE__到位的PID:

def api_function do 
    GenServer.call(__MODULE__, :api_function) 
end 

这样做的很好的属性,__MODULE__总是反映封闭模块的正确名称,即使您重命名它也是如此。

1

这取决于。如果你只是每个节点启动一个GenServer过程中,你可以这样调用它:

@doc """ 
If you want to call the server only from the current module. 
""" 
def local_call(message) do 
    GenServer.call(__MODULE__, message) 
end 

@doc """ 
If you want to call the server from another node on the network. 
""" 
def remote_call(message, server \\ nil) do 
    server = if server == nil, do: node(), else: server 
    GenServer.call({__MODULE__, server}, message) 
end 

如果从同一模块的几个过程,你需要额外的标识(例如,如果你有一个主管策略:simple_one_for_one按需生成GenServer)。

类似的东西我会建议使用:

  • :gproc命名过程。
  • :ets如果您需要额外信息来识别您的流程。

:gproc很棒,它可以与GenServer一起使用。您通常使用atom作为注册名称来命名您的进程。 :gproc允许你用一个任意的名字命名你的进程,例如一个元组。

比方说,在我的函数调用中,我有一个像{:id, id :: term}这样的服务器的复杂标识符,例如id可以是一个字符串。我可以开始我的GenServer,如:

defmodule MyServer do 
    use GenServer 

    def start_link(id) do 
    name = {:n, :l, {:id, id}} 
    GenServer.start_link(__MODULE__, %{}, name: {:via, :gproc, name}) 
    end 
    (...) 
end 

而且比原子不同的东西看看我的过程,就像我之前说的,例如一个字符串。所以,如果我启动我的服务器,如:

MyServer.start_link("My Random Hash") 

而且我有这样一个功能:

def f(id, message) do 
    improved_call(id, message) 
end 

defp improved_call(id, message, timeout \\ 5000) do 
    name = {:n, :l, {:id, id}} 
    case :gproc.where(name) do 
    undefined -> :error 
    pid -> GenServer.call(pid, message, timeout) 
end 

你可以用它来调用类似的过程:

MyServer.f("My Random Hash", {:message, "Hello"}) 

如果您的命名过程更复杂的是,您可以使用:ets来存储更复杂的状态。

如果你想使用:gproc你可以把它添加到您的mix.exs文件,如:

(...) 
defp deps do 
    [{:gproc, github: "uwiger/gproc"}] 
end 
(...) 

在一个侧面说明,永远,永远调用从handle_call/3GenServer.call/3。它会超时并在您的其他GenServer.call上执行DOS。 handle_call/3当时处理一条消息。

相关问题