“绿色线程”和Erlang的进程有什么区别?

11

在了解了Erlang的轻量级进程后,我相信它们是“绿色线程”(green threads)。直到我了解到绿色线程和Erlang进程之间存在差异,但我并不理解这些差异。

那么,这些实际上的差异是什么呢?


1
除Erlang之外,还有哪些系统是基于绿色进程的? - jldupont
这个问题在https://dev59.com/ZnRB5IYBdhLWcg3wXmVI#641873上有很多讨论。 - Gordon Guthrie
3个回答

14

绿色线程可以直接共享数据内存(当然需要同步)。

Erlang不使用“绿色线程”,而是使用更接近“绿色进程”的东西:进程不能直接共享数据内存,但可以通过“复制”它来进行共享(即拥有源数据的独立副本)。


10

简单地说,Erlang进程之间不能直接共享数据内存、只能在彼此之间传递值的说法是一种过于简化的描述。这更多是一种针对实现方式的描述,以及如何假装已经实现了这种方式,至少在除了性能问题之外的所有情况下。

Erlang对程序员的操作进行了一些语义限制。例如,值是不可变的,这意味着构造后无法更改它们。因此,我们可以轻松地让多个Erlang进程访问内存中的相同值,因为它们都无法改变它。这时就不需要使用锁了。

以下是在Erlang/OTP中完成此操作的显著情况:

  • 大型二进制(超过64字节)在一个特殊的二进制堆中被引用计数,并且当消息传递时通过引用传递。
  • 文本值放置在一个特殊的内存区域中,所有引用它们的进程都引用同一内存区域中的值(但是一旦该值在消息中发送,接收进程会创建一个副本)。
  • 每个节点都有一个全局原子表,原子值实际上是对该表的引用,这使得原子等式测试非常高效(比较指针而不是字符串)。
  • 实验性的erl -hybrid设置将进程堆和共享堆结合起来,在使用消息时,进程首先将值从进程堆复制到共享堆中。我在这里找到了一个有关混合堆的线程,该线程还解释了一些与该概念相关的问题。

另一个技巧是实际上更改值,但确保它不可见。这进一步解释了不可变值是一种语义限制。

以下是OTP/Erlang实际上会更改值的一些示例:

  • “最新”(R12)优化处理二进制语法,使您可以将值追加到二进制末尾,并实际上不必构造带有新尾部的完整新二进制。
  • 据说,使用立即set_element构建的新元组可以被编译器转换为在元组中实际就地更改该元素。

这些优化基于这样的理论:“如果一棵树倒在森林里,没有人听到,它真的发出声音吗?”。也就是说,引用不能逃逸到要被修改的对象中。否则,就会观察到它已经发生了变化。

这才是 Erlang 语义的关键所在,事物不应因其他进程的操作而产生副作用。我们称之为共享状态,我们非常不喜欢。

另一个过度简化的观点是,Erlang没有副作用。但如果有人问起,那就留着下次回答吧。


@Christian:写得很好。也许提前明确抽象边界会更有益,即实现视图与应用程序视图(即程序员可以访问的内容)。 - jldupont
1
BEAM确实会像所提到的那样改变值,但只是以一种受控制的方式进行,以便用户不会注意到。程序员无法显式地改变状态。 - rvirding

6
当人们反对称Erlang的进程为“绿色线程”时,他们反对的不是“绿色”部分,而是“线程”部分。
线程和进程之间的区别在于,线程仅有自己的指令指针,但共享其他所有内容(尤其是状态、内存、地址空间)。另一方面,进程完全隔离并且不共享任何东西。
Erlang的进程不共享任何东西,因此它们是真正的进程。然而,它们通常以“绿色”的方式实现。因此,从技术上讲,它们是“绿色进程”。
当我想强调轻量级实现时,我通常称它们为“绿色线程”,而当我想强调无共享语义时,我称它们为“进程”。这样我就不必解释我所说的“绿色进程”的含义了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接