程序似乎提前终止

Program Seems to Terminate Early

我觉得这是那些非常简单的问题之一,其中有一些我对语言不了解的地方。但我正在努力学习 Elixir,但我的程序并没有 运行 一路走完。我这里有一个最小的例子。

defmodule Foo do
  def run(0) do
    IO.puts("0")
  end
  def run(n) do
    IO.puts(to_string n)
    run(n - 1)
  end
  def go do
    run(100)
  end
end

# Foo.go
# spawn &Foo.go/0

现在,如果我取消注释底部的 Foo.go 行,并使用 elixir minimal.exs 取消注释 运行,那么我将得到预期的输出,即从 100 开始的所有数字到 0。如果我只取消注释 spawn &Foo.go/0 行,我始终得不到任何输出。

但是,如果我取消注释这两行和 运行 程序,我会得到从 100 到 0 的数字(从第一行开始),然后是前几个数字(通常大约是 100 到 96 左右)在程序由于某种原因终止之前。所以我真的不知道是什么导致进程在随机点终止。

值得指出的是,我之所以出现这种困惑是因为我试图使用mix编译一个更大的项目,当程序似乎启动时,只做了它的一小部分工作,然后终止,因为 mix 显然在 运行ning 之后停止了。所以我不确定 运行 Elixir 程序的惯用方法是什么,因为 mix 似乎在短时间内终止它。

spawn/1 将为 运行 函数创建一个新进程。虽然它们不一样,但您可以将 Erlang / Elixir 进程视为大多数其他语言中的线程。

因此,当您启动程序时,"main" 进程开始做一些工作。在你的例子中,它创建了一个新进程(我们称之为 "Process A")来输出从 100 到 0 的数字。但是,问题是 spawn/1 不会阻塞。这意味着 "main" 进程将继续执行,而不是等待 "Process A" 到 return。

所以发生的事情是您的 "main" 进程正在完成执行,从而结束整个程序。这对于我使用过的每种语言来说都是正常的。

如果你想在不同的进程中产生一些工作并确保它在结束你的程序之前完成执行,你有几个不同的选择。

您可以使用 Task 模块。与此类似的东西应该有效。

task = Task.async(&Foo.go/0)
Task.await(task)

您可以显式 send and receive 消息

defmodule Foo do
  def run(0, pid) do
    IO.puts("0")

    # This will send the message back to the "main" thread upon completion.
    send pid, {:done, self()}
  end
  def run(n, pid) do
    IO.puts(to_string n)
    run(n - 1, pid)
  end

  # We now pass along the pid of the "main" thread into the go function.
  def go(pid) do
    run(100, pid)
  end
end

# Use spawn/3 instead so we can pass in the "main" process pid.
pid = spawn(Foo, :go, [self()])

# This will block until it receives a message matching this pattern.
receive do
  # The ^ is the pin operator. It ensures that we match against the same pid as before.
  {:done, ^pid} -> :done
end

还有其他方法可以实现这一点。不幸的是,在不了解您要解决的问题的情况下,我只能提供基本的建议。

综上所述,mix 不会随意停止您的 运行ning 程序。无论出于何种原因,"main" 进程必须已完成执行。此外,mix 是一个构建工具,并不是您应该 运行 构建应用程序的真正方式(尽管您可以)。同样,在不知道您要做什么或查看您的代码的情况下,我不能给您更多信息。