Erlang 的核心概念

经过多年大致了解有关 Erlang(和 Elixir)的表面知识后,我最近一直在学习 Erlang(和 Elixir)。 现实世界中有很多教你如何编写 Erlang 或 Elixir 程序的东西,但在这里我想写一篇概念性的文章,我希望在开始之前阅读。 Erlang 以一种既简洁又经久不衰的方式结合了一组旧思想。

Erlang 的并发模型特别使用了我已经知道的三个概念:尾递归优化、轻量级进程和异步消息。

尾递归调用

尾递归调用是一种递归调用,不需要保留当前堆栈帧,因此可以通过运行时进行智能优化。 这是一个通过 Elixir 中的尾调用表示的无限循环示例(因为它对大多数人来说更容易阅读)。

def loop() do
  # do stuff
  loop()
end

当这个程序在 BEAM VM 上运行时,它被优化为更像 C 程序员所识别的命令式循环,如“while(true)”或“for(;;)”。 这在 Erlang/Elixir 中变得很重要,因为这些递归函数运行了很长时间,并形成了大多数长时间运行的 erlang 进程的主循环。

异步消息传递

Erlang 进程可以相互发送消息,并且消息发送到邮箱中。 让我们再次回到那个循环:

def loop() do
  result = receive do
    {:hello, name } -> name
    _ -> "?"
  end
  loop()
end

当此函数到达接收函数时,进程进入睡眠状态,直到其邮箱中有消息出现。 所以现在这个小东西开始有点像服务器了。

合并到一起

这些想法对我来说并不新鲜,但是 Erlang 从它们以及它们结合的方式中获得了很多东西。 回到我们的循环,让我们添加一个参数来存储我们想要在这个过程中保留的一些状态:

defmodule Counter do
  def loop(value) do
    next_value = receive do
      {:add, number} -> value + number
      {:subtract, number} -> value - number
    end
    IO.puts(next_value)
    loop(next_value)
  end
end

要创建这些计数器进程之一并向其发送消息,您需要执行以下操作:

pid = spawn fn () -> Counter.loop(0) end
send pid, {:add, 12}
# prints 12
send pid, {:subtract, 11}
# prints 1

现在我们有一个较小的计数器循环,客户端可以发送消息增加或减少它。 除了发送消息之外,没有其他方法可以获取内容,而 Erlang VM 可以一次创建数千个这些进程,因为它们不是您常用的 C++ 或 Java 的线程。

进程

Erlang 调度器与我遇到的其他任何东西都不一样。 Erlang 进程经常被抢占,VM 非常注意进程必须运行多少时间,我想这是因为 Erlang 在电话领域的起源。 它们也是超轻量级的,如果一个崩溃它不会对系统的其余部分造成问题。

在实践中,这意味着你在 Erlang 中启动了一个新进程来做一些很难在系统级的操作系统线程的事情。 大型 erlang 程序可以在一台机器上运行数百万个这样的进程,它们彼此不共享任何状态,并且它们都通过异步消息进行通信。 对单个进程进行推理很容易,因为这些循环只依赖于它们的参数,就像任何函数式程序一样。 此外,您不必担心共享内存或锁,并且更难创建数据竞争。

为什么这很有趣?

Erlang 从中得到的一个小例子是,我们唯一需要再次启动这个循环的就是计数器的值。 所以如果我们想在不关闭系统的情况下部署这个循环的新版本,系统可以启动一个新版本的函数并且知道它是安全的。 据我了解,这或多或少是 Erlang 著名的“热代码重载”功能的工作原理。

Erlang 使用进程的另一件我以前从未见过的事情是 Supervisors,它们是观察其他进程并在它们崩溃或遇到其他问题时重新启动它们的进程。 supervisor依赖于我在这里没有涉及的东西,称为process link。 在实践中,大型 Erlang 系统的工作人员会在工作中愉快地运行,一旦进入糟糕的状态就会崩溃。 这是一种非常不同的方式,我认为思考错误的方式要好得多。

Related Posts

2021 年你需要知道的关于 Erlang 的一切

今天,我们将看一个相当古老且有些古怪的东西。 你们大多数人可能没有注意到的语言。 虽然 Erlang 不像某些现代编程语言那样流行,但它安静地运行着 WhatsApp 和微信等每天为大量用户提供服务的应用程序。 在这篇文章中,我将告诉你关于这门语言的更多事情、它的历史,以及你是否应该考虑自己学习它。 ## 什么是 Erlang,它在哪里使用? Erl

Read More

Erlang JIT中基于类型的优化

这篇文章探讨了 Erlang/OTP 25 中基于类型的新优化,其中编译器将类型信息嵌入到 BEAM 文件中,以帮助JIT(即时编译器)生成更好的代码。 ## 两全其美 OTP 22 中引入的基于SSA的编译器处理步骤进行了复杂的类型分析,允许进行更多优化和更好的生成代码。然而,Erlang 编译器可以做什么样的优化是有限制的,因为 BEAM 文件必须

Read More

Erlang JIT之路

自从Erlang 存在,就一直有让它更快的需求和野心。这篇博文是一堂历史课,概述了主要的 Erlang 实现以及如何尝试提高 Erlang 的性能。 ## Prolog 解释器 Erlang 的第一个版本是在 1986 年在 Prolog 中实现的。那个版本的 Erlang 对于创建真正的应用程序来说太慢了,但它对于找出Erlang语言的哪些功能有用,哪

Read More