应用程序很简单。当它收到请求时,它会生成thread_listener并将其循环10次并传递索引(i)。 ndc_thread获取此数据(i)并返回核心。 Core正在循环并等待来自线程的消息,当它收到时,它会发送包含线程返回的消息的块。
问题是,在执行了1..10循环功能之后,它发送"结束"大块并关闭连接。 因此curl http://localhost:4000的输出是:" StartEnd" 期望的结果是:" Start12345678910End"
有没有办法保持连接打开并等到自定义超时或等待进程执行?
defmodule AlivePlug do
import Plug.Conn
def init(opts) do
opts
end
def call(conn, _opts) do
conn = send_chunked(conn, 200)
chunk(conn, "Start")
core_pid = spawn_link(fn -> core_listener(conn) end)
thread = spawn_link(fn -> thread_listener end)
1..10 |> Enum.each(fn i -> send thread, {core_pid, i} end)
chunk(conn, "End")
conn
end
defp core_listener(conn) do
receive do
{status, i} ->
_conn = chunk(conn, Integer.to_string(i))
core_listener(conn)
end
end
defp thread_listener do
receive do
{core_pid, i} ->
send core_pid, {:ok, i}
thread_listener
_ ->
thread_listener
end
end
end
这是工作申请 只需运行并使用邮递员或卷曲http://localhost:4000 https://github.com/programisti/keep-alive-elixir
答案 0 :(得分:1)
好吧,问题是你生成了thread
进程,向它发送了一些数据,但是从不等待它有机会工作,然后你持有连接的函数就结束了。
让我们从一个非常幼稚的解决方案开始:
1..10 |> Enum.each(fn i -> send thread, {core_pid, i} end)
Process.sleep 5000
chunk(conn, "End")
如果我们睡了5秒钟,那么thread
就应该有足够的时间完成所有工作。这是一个糟糕的解决方案,因为如果你循环到10000然后5秒可能太短而且1..10
5秒可能太长了。而且,你不应该相信盲目的时机。 理论上系统可能会等待30秒一次,再等待10毫秒,以便在CPU上安排thread
。
现在让我们根据进程邮箱是FIFO的事实做一个真正的解决方案。
首先,我们将添加thread_listener
理解的其他类型的消息:
defp thread_listener do
receive do
{core_pid, i} ->
send core_pid, {:ok, i}
thread_listener
{:please_ack, pid} ->
send pid :ack
thread_listener
_ ->
thread_listener
end
end
接下来,让我们用更聪明的东西替换Process.sleep
1..10 |> Enum.each(fn i -> send thread, {core_pid, i} end)
send thread, {:please_ack, self}
receive do
:ack ->
ok
end
chunk(conn, "End")
现在我们将第11条消息放入thread
的邮箱,然后等待确认。一旦处理了所有11条消息,我们将收到一条消息并能够继续。如果thread
处理每个消息的时间或者在它开始之前是否有10秒的暂停,则无关紧要。 (这些数字是双曲线的,但我试图强调,在多线程编程中,你不能依赖于时序。)
您现在可能已经注意到,我们实施的内容实际上并没有以有用的方式使用流程。你从主线程中产生了两个线程,它们都处于空闲状态,直到主线程向它们发送数据,然后你的主线程处于空闲状态,直到产生的线程完成为止。在主线程中完成工作会更清楚。这个例子实际上需要线程太简单了,我无法发现你计划如何扩展它,但我希望这给你一些关于线程编程的一般帮助。