男模:为什么erlang是特别的?或者,为什么你需要另一种语言呢?

时间:2022-08-13 07:07:39

I've been looking into learning erlang, and as a result, have been reading (okay, skimming) about the actor model.

我一直在研究学习erlang,因此,我一直在阅读(好的,略读)关于演员模型的文章。

From what I understand, the actor model is simply a set of functions (run within lightweight threads called "processes" in erlang), which communicate with each other only via message passing.

根据我的理解,actor模型只是一组函数(在erlang中称为“process”的轻量级线程中运行),它们只通过消息传递彼此通信。

This seems fairly trivial to implement in C++, or any other language:

在c++或任何其他语言中实现这一点似乎都不重要:

class BaseActor {
    std::queue<BaseMessage*> messages;
    CriticalSection messagecs;
    BaseMessage* Pop();
public:
    void Push(BaseMessage* message)
    {
        auto scopedlock = messagecs.AquireScopedLock();
        messagecs.push(message);
    }
    virtual void ActorFn() = 0;
    virtual ~BaseActor() {} = 0;
}

With each of your processes being an instance of a derived BaseActor. Actors communicate with each other only via message-passing. (namely, pushing). Actors register themselves with a central map on initialization which allows other actors to find them, and allows a central function to run through them.

您的每个进程都是派生的BaseActor的实例。演员之间只通过消息传递进行交流。(即,推动)。参与者通过初始化的*映射注册自己,该映射允许其他参与者找到它们,并允许*函数运行它们。

Now, I understand I'm missing, or rather, glossing over one important issue here, namely: lack of yielding means a single Actor can unfairly consume excessive time. But are cross-platform coroutines the primary thing that makes this hard in C++? (Windows for instance has fibers.)

现在,我明白我忽略了,或者更确切地说,忽略了一个重要的问题,那就是:缺乏让步意味着一个演员可以不公平地浪费过多的时间。但是跨平台的联锁是使c++难以实现的主要原因吗?(比如窗户有纤维。)

Is there anything else I'm missing, though, or is the model really this obvious?

不过,我还遗漏了什么吗?或者这个模型真的如此明显吗?

I'm definitely not trying to start a flame war here, I just want to understand what I'm missing, as this is essentially what I already do to be able to somewhat reason about concurrent code.

我并不是想在这里引发一场激烈的争论,我只是想了解我遗漏了什么,因为这基本上是我已经在做的,可以对并发代码进行一些推理。

6 个解决方案

#1


81  

The C++ code does not deal with fairness, isolation, fault detection or distribution which are all things which Erlang brings as part of its actor model.

c++代码不处理公平性、隔离性、故障检测或分布,这些都是Erlang作为它的actor模型的一部分带来的。

  • No actor is allowed to starve any other actor (fairness)
  • 任何演员都不能饿死其他演员(公平地说)
  • If one actor crashes, it should only affect that actor (isolation)
  • 如果一个行为人崩溃,它应该只影响那个行为人(隔离)
  • If one actor crashes, other actors should be able to detect and react to that crash (fault detection)
  • 如果一个参与者崩溃,其他参与者应该能够检测并对该崩溃做出反应(故障检测)
  • Actors should be able to communicate over a network as if they were on the same machine (distribution)
  • 参与者应该能够通过网络进行通信,就像在同一台机器上一样(分布)

Also the beam SMP emulator brings JIT scheduling of the actors, moving them to the core which is at the moment the one with least utilization and also hibernates the threads on certain cores if they are no longer needed.

此外,beam SMP仿真器引入了参与者的JIT调度,将它们移动到核心中,即当前利用率最低的核心,并且如果不再需要某些核心上的线程也会休眠。

In addition all the libraries and tools written in Erlang can assume that this is the way the world works and be designed accordingly.

此外,用Erlang编写的所有库和工具都可以假定这是世界的工作方式,并据此进行设计。

These things are not impossible to do in C++, but they get increasingly hard if you add the fact that Erlang works on almost all of the major hw and os configurations.

在c++中,这些事情不是不可能完成的,但是如果您再加上Erlang几乎可以处理所有主要的hw和os配置,它们就会变得越来越困难。

edit: Just found a description by Ulf Wiger about what he sees erlang style concurrency as.

编辑:刚刚找到Ulf Wiger对erlang风格并发的描述。

#2


29  

I don't like to quote myself, but from Virding's First Rule of Programming

我不喜欢引用我自己的话,但是从Virding的编程第一条规则开始

Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified bug-ridden slow implementation of half of Erlang.

在另一种语言中,任何足够复杂的并发程序都包含了一种特殊的、特定的bug,它执行了Erlang的一半。

With respect to Greenspun. Joe (Armstrong) has a similar rule.

格林斯潘。乔(阿姆斯特朗)也有类似的规则。

The problem is not to implement actors, that's not that difficult. The problem is to get everything working together: processes, communication, garbage collection, language primitives, error handling, etc ... For example using OS threads scales badly so you need to do it yourself. It would be like trying to "sell" an OO language where you can only have 1k objects and they are heavy to create and use. From our point of view concurrency is the basic abstraction for structuring applications.

问题不在于实现参与者,这并不难。问题是让所有的东西一起工作:进程、通信、垃圾收集、语言原语、错误处理等等……例如,使用OS线程的伸缩性很差,所以您需要自己来做。这就像试图“推销”一种OO语言,在这种语言中,你只能拥有1k个对象,而且创建和使用它们非常困难。从我们的观点来看,并发是构建应用程序的基本抽象。

Getting carried away so I will stop here.

我有点忘乎所以了,就到这里吧。

#3


21  

This is actually an excellent question, and has received excellent answers that perhaps are yet unconvincing.

这实际上是一个很好的问题,并且已经得到了很好的答案,也许还不能令人信服。

To add shade and emphasis to the other great answers already here, consider what Erlang takes away (compared to traditional general purpose languages such as C/C++) in order to achieve fault-tolerance and uptime.

为了给这里已经提到的其他伟大的答案添加阴影和强调,考虑一下Erlang去掉了什么(与传统通用语言如C/ c++相比),以实现容错和正常运行时间。

First, it takes away locks. Joe Armstrong's book lays out this thought experiment: suppose your process acquires a lock and then immediately crashes (a memory glitch causes the process to crash, or the power fails to part of the system). The next time a process waits for that same lock, the system has just deadlocked. This could be an obvious lock, as in the AquireScopedLock() call in the sample code; or it could be an implicit lock acquired on your behalf by a memory manager, say when calling malloc() or free().

首先,它带走了锁。Joe Armstrong的书阐述了这个思想实验:假设您的进程获得了一个锁,然后立即崩溃(内存故障导致进程崩溃,或者是系统的部分功能失败)。下次进程等待同样的锁时,系统就会死锁。这可能是一个明显的锁,如示例代码中的AquireScopedLock()调用;或者它可以是内存管理器代表您获取的隐式锁,例如在调用malloc()或free()时。

In any case, your process crash has now halted the entire system from making progress. Fini. End of story. Your system is dead. Unless you can guarantee that every library you use in C/C++ never calls malloc and never acquires a lock, your system is not fault tolerant. Erlang systems can and do kill processes at will when under heavy load in order make progress, so at scale your Erlang processes must be killable (at any single point of execution) in order to maintain throughput.

无论如何,您的进程崩溃现在已经阻止了整个系统取得进展。菲尼。故事结束了。您的系统已经死了。除非您能够保证在C/ c++中使用的每个库都不会调用malloc,也不会获得锁,否则您的系统不会容错。Erlang系统可以并且确实可以在重载时随意杀死进程,以便取得进展,因此在规模上,为了保持吞吐量,您的Erlang进程必须是可杀的(在任何执行点)。

There is a partial workaround: using leases everywhere instead of locks, but you have no guarantee that all the libraries you utilize also do this. And the logic and reasoning about correctness gets really hairy quickly. Moreover leases recover slowly (after the timeout expires), so your entire system just got really slow in the face of failure.

这里有一个局部的解决方案:到处使用租约而不是锁,但是您不能保证您使用的所有库都这样做。关于正确性的逻辑和推理很快就变得非常复杂。此外,租约恢复缓慢(在超时过期后),因此在面临失败时,整个系统的速度非常慢。

Second, Erlang takes away static typing, which in turn enables hot code swapping and running two versions of the same code simultaneously. This means you can upgrade your code at runtime without stopping the system. This is how systems stay up for nine 9's or 32 msec of downtime/year. They are simply upgraded in place. Your C++ functions will have to be manually re-linked in order to be upgraded, and running two versions at the same time is not supported. Code upgrades require system downtime, and if you have a large cluster that cannot run more than one version of code at once, you'll need to take the entire cluster down at once. Ouch. And in the telecom world, not tolerable.

其次,Erlang去掉了静态类型,从而允许热代码交换,并同时运行同一代码的两个版本。这意味着您可以在运行时升级代码,而无需停止系统。这就是系统如何保持9秒或32毫秒的停机时间/年。它们只是在适当的地方升级。您的c++函数必须手动重新链接才能升级,并且不支持同时运行两个版本。代码升级需要系统停机,如果您有一个大型集群,不能同时运行多个版本的代码,那么您需要立即将整个集群关闭。哎哟。在电信世界里,是不可容忍的。

In addition Erlang takes away shared memory and shared shared garbage collection; each light weight process is garbage collected independently. This is a simple extension of the first point, but emphasizes that for true fault tolerance you need processes that are not interlocked in terms of dependencies. It means your GC pauses compared to java are tolerable (small instead of pausing a half-hour for a 8GB GC to complete) for big systems.

此外,Erlang带走共享内存和共享垃圾收集;每个轻量级过程都是独立收集的垃圾。这是第一点的一个简单扩展,但强调对于真正的容错,您需要的进程不是在依赖关系方面相互关联的。这意味着与java相比,您的GC暂停是可以接受的(对于大型系统来说,小而不是为8GB GC暂停半小时)。

#4


14  

There are actual actor libraries for C++:

c++有实际的演员库:

And a list of some libraries for other languages.

以及其他语言的一些库的列表。

#5


3  

It is a lot less about the actor model and a lot more about how hard it is to properly write something analogous to OTP in C++. Also, different operating systems provide radically different debugging and system tooling, and Erlang's VM and several language constructs support a uniform way of figuring out just what all those processes are up to which would be very hard to do in a uniform way (or maybe do at all) across several platforms. (It is important to remember that Erlang/OTP predates the current buzz over the term "actor model", so in some cases these sort of discussions are comparing apples and pterodactyls; great ideas are prone to independent invention.)

它更多的是关于如何在c++中正确地写出类似于OTP的东西。同时,不同的操作系统提供完全不同的调试和系统工具,和Erlang的VM和几语言结构支持的统一的方式指出所有这些流程在做什么这将是很难做以统一的方式(或者)跨多个平台。(重要的是要记住,Erlang/OTP早于“演员模型”这一术语的流行,所以在某些情况下,这种讨论是在比较苹果和翼手龙;伟大的想法往往是独立的发明。

All this means that while you certainly can write an "actor model" suite of programs in another language (I know, I have done this for a long time in Python, C and Guile without realizing it before I encountered Erlang, including a form of monitors and links, and before I'd ever heard the term "actor model"), understanding how the processes your code actually spawns and what is happening amongst them is extremely difficult. Erlang enforces rules that an OS simply can't without major kernel overhauls -- kernel overhauls that would probably not be beneficial overall. These rules manifest themselves as both general restrictions on the programmer (which can always be gotten around if you really need to) and basic promises the system guarantees for the programmer (which can be deliberately broken if you really need to also).

这一切意味着你当然可以写一个“角色模型”程序套件用另一种语言(我知道,我这样做很长一段时间在Python中,C和狡诈而没有意识到这一点之前,我遇到了Erlang,包括显示器和链接的一种形式,之前,我听说过这个术语“角色模型”),理解代码的流程是如何产生并在它们之间发生了什么是极其困难的。Erlang强制执行操作系统必须进行重大内核检查的规则——内核检查,从总体上来说,这可能是没有好处的。这些规则表现为对程序员的一般限制(如果确实需要,可以随时绕过)和对程序员的系统保证的基本保证(如果确实需要,可以故意打破)。

For example, it enforces that two processes cannot share state to protect you from side effects. This does not mean that every function must be "pure" in the sense that everything is referentially transparent (obviously not, though making as much of your program referentially transparent as practical is a clear design goal of most Erlang projects), but rather that two processes aren't constantly creating race conditions related to shared state or contention. (This is more what "side effects" means in the context of Erlang, by the way; knowing that may help you decipher some of the discussion questioning whether Erlang is "really functional or not" when compared with Haskell or toy "pure" languages.)

例如,它强制两个进程不能共享状态以保护您不受副作用的影响。这并不意味着每个函数都必须是“纯”,一切都是透明的优先(显然不是,虽然使尽可能多的程序并包含透明的实际是大多数Erlang项目)的一个明确的设计目标,而是两个过程不是不断创建竞态条件相关的共享状态或争论。(顺便说一句,在Erlang的语境中,这更像是“副作用”的意思;知道了这一点,你就可以理解一些讨论了:与Haskell或“纯”语言相比,Erlang是否“真正有用”?

On the other hand, the Erlang runtime guarantees delivery of messages. This is something sorely missed in an environment where you must communicate purely over unmanaged ports, pipes, shared memory and common files which the OS kernel is the only one managing (and OS kernel management of these resources is necessarily extremely minimal compared to what the Erlang runtime provides). This doesn't meant that Erlang guarantees RPC (anyway, message passing is not RPC, nor is it method invocation!), it doesn't promise that your message is addressed correctly, and it doesn't promise that a process you're trying to send a message to exists or is alive, either. It just guarantees delivery if the thing your sending to happens to be valid at that moment.

另一方面,Erlang运行时保证消息的传递。在这样的环境中,您必须纯粹通过非托管端口、管道、共享内存和公共文件进行通信,而OS内核是惟一管理这些资源的操作系统内核(与Erlang运行时提供的操作系统内核相比,这些资源的操作系统内核管理必须非常小)。这并不意味着Erlang保证RPC(无论如何,消息传递不是RPC,也不是方法调用!它只是保证你发送的东西在那个时候有效。

Built on this promise is the promise that monitors and links are accurate. And based on that the Erlang runtime makes the entire concept of "network cluster" sort of melt away once you grasp what is going on with the system (and how to use erl_connect...). This permits you to hop over a set of tricky concurrency cases already, which gives one a big head start on coding for the successful case instead of getting mired in the swamp of defensive techniques required for naked concurrent programming.

基于这一承诺的承诺是,监控和链接是准确的。在此基础上,Erlang运行时使“网络集群”的整个概念在您了解了系统的运行情况(以及如何使用erl_connect…)之后就消失了。这允许您跳过一组棘手的并发性案例,这为成功案例的编码提供了一个重要的开端,而不是陷入对裸并发编程所需要的防御技术的泥潭。

So its not really about needing Erlang, the language, its about the runtime and OTP already existing, being expressed in a rather clean way, and implementing anything close to it in another language being extremely hard. OTP is just a hard act to follow. In the same vein, we don't really need C++, either, we could just stick to raw binary input, Brainfuck and consider Assembler our high level language. We also don't need trains or ships, as we all know how to walk and swim.

所以这并不是说需要Erlang,语言,它是关于运行时和OTP已经存在的,以一种相当干净的方式表达,并且用另一种语言实现任何接近它的东西都是极其困难的。OTP只是一个很难遵循的行为。同样地,我们也不需要c++,我们可以只使用原始的二进制输入,脑力劳动并考虑汇编我们的高级语言。我们也不需要火车或轮船,因为我们都知道如何走路和游泳。

All that said, the VM's bytecode is well documented, and a number of alternative languages have emerged that compile to it or work with the Erlang runtime. If we break the question into a language/syntax part ("Do I have to understand Moon Runes to do concurrency?") and a platform part ("Is OTP the most mature way to do concurrency, and will it guide me around the trickiest, most common pitfalls to be found in a concurrent, distributed environment?") then the answer is ("no", "yes").

尽管如此,VM的字节码已经被很好地记录下来了,并且已经出现了许多替代语言来编译它或使用Erlang运行时。如果我们的问题分解成一个语言/语法部分(“我必须了解月球并发符文?”)和一个平台(“是并发OTP最成熟的方法,并将它引导我最棘手,最常见的缺陷被发现在一个并发分布式环境?”)然后答案是(“不”,“是”)。

#6


2  

Casablanca is another new kid on the actor model block. A typical asynchronous accept looks like this:

《卡萨布兰卡》是另一个新角色。典型的异步接受如下所示:

PID replyTo;
NameQuery request;
accept_request().then([=](std::tuple<NameQuery,PID> request)
{
   if (std::get<0>(request) == FirstName)
       std::get<1>(request).send("Niklas");
   else
       std::get<1>(request).send("Gustafsson");
}

(Personally, I find that CAF does a better job at hiding the pattern matching behind a nice interface.)

(就我个人而言,我发现CAF在隐藏一个漂亮界面的模式匹配方面做得更好。)

#1


81  

The C++ code does not deal with fairness, isolation, fault detection or distribution which are all things which Erlang brings as part of its actor model.

c++代码不处理公平性、隔离性、故障检测或分布,这些都是Erlang作为它的actor模型的一部分带来的。

  • No actor is allowed to starve any other actor (fairness)
  • 任何演员都不能饿死其他演员(公平地说)
  • If one actor crashes, it should only affect that actor (isolation)
  • 如果一个行为人崩溃,它应该只影响那个行为人(隔离)
  • If one actor crashes, other actors should be able to detect and react to that crash (fault detection)
  • 如果一个参与者崩溃,其他参与者应该能够检测并对该崩溃做出反应(故障检测)
  • Actors should be able to communicate over a network as if they were on the same machine (distribution)
  • 参与者应该能够通过网络进行通信,就像在同一台机器上一样(分布)

Also the beam SMP emulator brings JIT scheduling of the actors, moving them to the core which is at the moment the one with least utilization and also hibernates the threads on certain cores if they are no longer needed.

此外,beam SMP仿真器引入了参与者的JIT调度,将它们移动到核心中,即当前利用率最低的核心,并且如果不再需要某些核心上的线程也会休眠。

In addition all the libraries and tools written in Erlang can assume that this is the way the world works and be designed accordingly.

此外,用Erlang编写的所有库和工具都可以假定这是世界的工作方式,并据此进行设计。

These things are not impossible to do in C++, but they get increasingly hard if you add the fact that Erlang works on almost all of the major hw and os configurations.

在c++中,这些事情不是不可能完成的,但是如果您再加上Erlang几乎可以处理所有主要的hw和os配置,它们就会变得越来越困难。

edit: Just found a description by Ulf Wiger about what he sees erlang style concurrency as.

编辑:刚刚找到Ulf Wiger对erlang风格并发的描述。

#2


29  

I don't like to quote myself, but from Virding's First Rule of Programming

我不喜欢引用我自己的话,但是从Virding的编程第一条规则开始

Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified bug-ridden slow implementation of half of Erlang.

在另一种语言中,任何足够复杂的并发程序都包含了一种特殊的、特定的bug,它执行了Erlang的一半。

With respect to Greenspun. Joe (Armstrong) has a similar rule.

格林斯潘。乔(阿姆斯特朗)也有类似的规则。

The problem is not to implement actors, that's not that difficult. The problem is to get everything working together: processes, communication, garbage collection, language primitives, error handling, etc ... For example using OS threads scales badly so you need to do it yourself. It would be like trying to "sell" an OO language where you can only have 1k objects and they are heavy to create and use. From our point of view concurrency is the basic abstraction for structuring applications.

问题不在于实现参与者,这并不难。问题是让所有的东西一起工作:进程、通信、垃圾收集、语言原语、错误处理等等……例如,使用OS线程的伸缩性很差,所以您需要自己来做。这就像试图“推销”一种OO语言,在这种语言中,你只能拥有1k个对象,而且创建和使用它们非常困难。从我们的观点来看,并发是构建应用程序的基本抽象。

Getting carried away so I will stop here.

我有点忘乎所以了,就到这里吧。

#3


21  

This is actually an excellent question, and has received excellent answers that perhaps are yet unconvincing.

这实际上是一个很好的问题,并且已经得到了很好的答案,也许还不能令人信服。

To add shade and emphasis to the other great answers already here, consider what Erlang takes away (compared to traditional general purpose languages such as C/C++) in order to achieve fault-tolerance and uptime.

为了给这里已经提到的其他伟大的答案添加阴影和强调,考虑一下Erlang去掉了什么(与传统通用语言如C/ c++相比),以实现容错和正常运行时间。

First, it takes away locks. Joe Armstrong's book lays out this thought experiment: suppose your process acquires a lock and then immediately crashes (a memory glitch causes the process to crash, or the power fails to part of the system). The next time a process waits for that same lock, the system has just deadlocked. This could be an obvious lock, as in the AquireScopedLock() call in the sample code; or it could be an implicit lock acquired on your behalf by a memory manager, say when calling malloc() or free().

首先,它带走了锁。Joe Armstrong的书阐述了这个思想实验:假设您的进程获得了一个锁,然后立即崩溃(内存故障导致进程崩溃,或者是系统的部分功能失败)。下次进程等待同样的锁时,系统就会死锁。这可能是一个明显的锁,如示例代码中的AquireScopedLock()调用;或者它可以是内存管理器代表您获取的隐式锁,例如在调用malloc()或free()时。

In any case, your process crash has now halted the entire system from making progress. Fini. End of story. Your system is dead. Unless you can guarantee that every library you use in C/C++ never calls malloc and never acquires a lock, your system is not fault tolerant. Erlang systems can and do kill processes at will when under heavy load in order make progress, so at scale your Erlang processes must be killable (at any single point of execution) in order to maintain throughput.

无论如何,您的进程崩溃现在已经阻止了整个系统取得进展。菲尼。故事结束了。您的系统已经死了。除非您能够保证在C/ c++中使用的每个库都不会调用malloc,也不会获得锁,否则您的系统不会容错。Erlang系统可以并且确实可以在重载时随意杀死进程,以便取得进展,因此在规模上,为了保持吞吐量,您的Erlang进程必须是可杀的(在任何执行点)。

There is a partial workaround: using leases everywhere instead of locks, but you have no guarantee that all the libraries you utilize also do this. And the logic and reasoning about correctness gets really hairy quickly. Moreover leases recover slowly (after the timeout expires), so your entire system just got really slow in the face of failure.

这里有一个局部的解决方案:到处使用租约而不是锁,但是您不能保证您使用的所有库都这样做。关于正确性的逻辑和推理很快就变得非常复杂。此外,租约恢复缓慢(在超时过期后),因此在面临失败时,整个系统的速度非常慢。

Second, Erlang takes away static typing, which in turn enables hot code swapping and running two versions of the same code simultaneously. This means you can upgrade your code at runtime without stopping the system. This is how systems stay up for nine 9's or 32 msec of downtime/year. They are simply upgraded in place. Your C++ functions will have to be manually re-linked in order to be upgraded, and running two versions at the same time is not supported. Code upgrades require system downtime, and if you have a large cluster that cannot run more than one version of code at once, you'll need to take the entire cluster down at once. Ouch. And in the telecom world, not tolerable.

其次,Erlang去掉了静态类型,从而允许热代码交换,并同时运行同一代码的两个版本。这意味着您可以在运行时升级代码,而无需停止系统。这就是系统如何保持9秒或32毫秒的停机时间/年。它们只是在适当的地方升级。您的c++函数必须手动重新链接才能升级,并且不支持同时运行两个版本。代码升级需要系统停机,如果您有一个大型集群,不能同时运行多个版本的代码,那么您需要立即将整个集群关闭。哎哟。在电信世界里,是不可容忍的。

In addition Erlang takes away shared memory and shared shared garbage collection; each light weight process is garbage collected independently. This is a simple extension of the first point, but emphasizes that for true fault tolerance you need processes that are not interlocked in terms of dependencies. It means your GC pauses compared to java are tolerable (small instead of pausing a half-hour for a 8GB GC to complete) for big systems.

此外,Erlang带走共享内存和共享垃圾收集;每个轻量级过程都是独立收集的垃圾。这是第一点的一个简单扩展,但强调对于真正的容错,您需要的进程不是在依赖关系方面相互关联的。这意味着与java相比,您的GC暂停是可以接受的(对于大型系统来说,小而不是为8GB GC暂停半小时)。

#4


14  

There are actual actor libraries for C++:

c++有实际的演员库:

And a list of some libraries for other languages.

以及其他语言的一些库的列表。

#5


3  

It is a lot less about the actor model and a lot more about how hard it is to properly write something analogous to OTP in C++. Also, different operating systems provide radically different debugging and system tooling, and Erlang's VM and several language constructs support a uniform way of figuring out just what all those processes are up to which would be very hard to do in a uniform way (or maybe do at all) across several platforms. (It is important to remember that Erlang/OTP predates the current buzz over the term "actor model", so in some cases these sort of discussions are comparing apples and pterodactyls; great ideas are prone to independent invention.)

它更多的是关于如何在c++中正确地写出类似于OTP的东西。同时,不同的操作系统提供完全不同的调试和系统工具,和Erlang的VM和几语言结构支持的统一的方式指出所有这些流程在做什么这将是很难做以统一的方式(或者)跨多个平台。(重要的是要记住,Erlang/OTP早于“演员模型”这一术语的流行,所以在某些情况下,这种讨论是在比较苹果和翼手龙;伟大的想法往往是独立的发明。

All this means that while you certainly can write an "actor model" suite of programs in another language (I know, I have done this for a long time in Python, C and Guile without realizing it before I encountered Erlang, including a form of monitors and links, and before I'd ever heard the term "actor model"), understanding how the processes your code actually spawns and what is happening amongst them is extremely difficult. Erlang enforces rules that an OS simply can't without major kernel overhauls -- kernel overhauls that would probably not be beneficial overall. These rules manifest themselves as both general restrictions on the programmer (which can always be gotten around if you really need to) and basic promises the system guarantees for the programmer (which can be deliberately broken if you really need to also).

这一切意味着你当然可以写一个“角色模型”程序套件用另一种语言(我知道,我这样做很长一段时间在Python中,C和狡诈而没有意识到这一点之前,我遇到了Erlang,包括显示器和链接的一种形式,之前,我听说过这个术语“角色模型”),理解代码的流程是如何产生并在它们之间发生了什么是极其困难的。Erlang强制执行操作系统必须进行重大内核检查的规则——内核检查,从总体上来说,这可能是没有好处的。这些规则表现为对程序员的一般限制(如果确实需要,可以随时绕过)和对程序员的系统保证的基本保证(如果确实需要,可以故意打破)。

For example, it enforces that two processes cannot share state to protect you from side effects. This does not mean that every function must be "pure" in the sense that everything is referentially transparent (obviously not, though making as much of your program referentially transparent as practical is a clear design goal of most Erlang projects), but rather that two processes aren't constantly creating race conditions related to shared state or contention. (This is more what "side effects" means in the context of Erlang, by the way; knowing that may help you decipher some of the discussion questioning whether Erlang is "really functional or not" when compared with Haskell or toy "pure" languages.)

例如,它强制两个进程不能共享状态以保护您不受副作用的影响。这并不意味着每个函数都必须是“纯”,一切都是透明的优先(显然不是,虽然使尽可能多的程序并包含透明的实际是大多数Erlang项目)的一个明确的设计目标,而是两个过程不是不断创建竞态条件相关的共享状态或争论。(顺便说一句,在Erlang的语境中,这更像是“副作用”的意思;知道了这一点,你就可以理解一些讨论了:与Haskell或“纯”语言相比,Erlang是否“真正有用”?

On the other hand, the Erlang runtime guarantees delivery of messages. This is something sorely missed in an environment where you must communicate purely over unmanaged ports, pipes, shared memory and common files which the OS kernel is the only one managing (and OS kernel management of these resources is necessarily extremely minimal compared to what the Erlang runtime provides). This doesn't meant that Erlang guarantees RPC (anyway, message passing is not RPC, nor is it method invocation!), it doesn't promise that your message is addressed correctly, and it doesn't promise that a process you're trying to send a message to exists or is alive, either. It just guarantees delivery if the thing your sending to happens to be valid at that moment.

另一方面,Erlang运行时保证消息的传递。在这样的环境中,您必须纯粹通过非托管端口、管道、共享内存和公共文件进行通信,而OS内核是惟一管理这些资源的操作系统内核(与Erlang运行时提供的操作系统内核相比,这些资源的操作系统内核管理必须非常小)。这并不意味着Erlang保证RPC(无论如何,消息传递不是RPC,也不是方法调用!它只是保证你发送的东西在那个时候有效。

Built on this promise is the promise that monitors and links are accurate. And based on that the Erlang runtime makes the entire concept of "network cluster" sort of melt away once you grasp what is going on with the system (and how to use erl_connect...). This permits you to hop over a set of tricky concurrency cases already, which gives one a big head start on coding for the successful case instead of getting mired in the swamp of defensive techniques required for naked concurrent programming.

基于这一承诺的承诺是,监控和链接是准确的。在此基础上,Erlang运行时使“网络集群”的整个概念在您了解了系统的运行情况(以及如何使用erl_connect…)之后就消失了。这允许您跳过一组棘手的并发性案例,这为成功案例的编码提供了一个重要的开端,而不是陷入对裸并发编程所需要的防御技术的泥潭。

So its not really about needing Erlang, the language, its about the runtime and OTP already existing, being expressed in a rather clean way, and implementing anything close to it in another language being extremely hard. OTP is just a hard act to follow. In the same vein, we don't really need C++, either, we could just stick to raw binary input, Brainfuck and consider Assembler our high level language. We also don't need trains or ships, as we all know how to walk and swim.

所以这并不是说需要Erlang,语言,它是关于运行时和OTP已经存在的,以一种相当干净的方式表达,并且用另一种语言实现任何接近它的东西都是极其困难的。OTP只是一个很难遵循的行为。同样地,我们也不需要c++,我们可以只使用原始的二进制输入,脑力劳动并考虑汇编我们的高级语言。我们也不需要火车或轮船,因为我们都知道如何走路和游泳。

All that said, the VM's bytecode is well documented, and a number of alternative languages have emerged that compile to it or work with the Erlang runtime. If we break the question into a language/syntax part ("Do I have to understand Moon Runes to do concurrency?") and a platform part ("Is OTP the most mature way to do concurrency, and will it guide me around the trickiest, most common pitfalls to be found in a concurrent, distributed environment?") then the answer is ("no", "yes").

尽管如此,VM的字节码已经被很好地记录下来了,并且已经出现了许多替代语言来编译它或使用Erlang运行时。如果我们的问题分解成一个语言/语法部分(“我必须了解月球并发符文?”)和一个平台(“是并发OTP最成熟的方法,并将它引导我最棘手,最常见的缺陷被发现在一个并发分布式环境?”)然后答案是(“不”,“是”)。

#6


2  

Casablanca is another new kid on the actor model block. A typical asynchronous accept looks like this:

《卡萨布兰卡》是另一个新角色。典型的异步接受如下所示:

PID replyTo;
NameQuery request;
accept_request().then([=](std::tuple<NameQuery,PID> request)
{
   if (std::get<0>(request) == FirstName)
       std::get<1>(request).send("Niklas");
   else
       std::get<1>(request).send("Gustafsson");
}

(Personally, I find that CAF does a better job at hiding the pattern matching behind a nice interface.)

(就我个人而言,我发现CAF在隐藏一个漂亮界面的模式匹配方面做得更好。)