阻塞通道与异步消息传递

10 投票
2 回答
1209 浏览
提问于 2025-04-15 19:07

我注意到有两种“消息传递”的方法。一种是我在Erlang中看到的,另一种来自Stackless Python。根据我的理解,它们之间的区别如下:

Erlang风格 - 消息会被发送并排队到接收进程的邮箱里。然后这些消息会按照先进先出的顺序被取出。一旦第一个进程发送了消息,它就可以继续执行其他操作。

Python风格 - 进程A准备发送消息给进程B,但B此时正在做其他事情,所以A会被暂停,直到B准备好接收消息。一旦B打开了接收通道,A就会发送数据,然后它们都可以继续执行。

我觉得Erlang方法的好处在于没有任何进程会被阻塞。如果B永远无法接收消息,A仍然可以继续运行。不过,我在自己写的一些程序中发现,Erlang的消息箱可能会堆满成百上千条消息,因为消息的到达速度比处理速度快。

我还没有在这两种框架/语言中写过大型程序,所以我想知道你们的经验是什么,是否应该对此感到担忧。

是的,我知道这有点抽象,但我也在寻找一些比较抽象的答案。

2 个回答

4

简单来说,这里讲的是无界队列和有界队列的区别。一个没有栈的通道可以看作是一个大小为0的队列。

有界队列容易出现死锁的情况。比如两个线程或进程想互相发送消息,但它们的队列都满了,这样就会卡住。

无界队列的失败方式更微妙。就像你提到的,一个很大的邮箱可能无法满足延迟要求。如果继续增加容量,最终会溢出;因为没有真正无限的内存,所以这其实就是一个有界队列,只不过限制非常大,当满了就会中断处理。

那么哪种更好呢?这很难说,没有简单的答案。

8

我在Erlang编程中的经验是,当你预期消息发送速度很高(也就是说,发送消息的速度比接收消息的速度快)时,你需要自己添加流量控制。下面是一个简单的场景:

  • 接收方会:发送消息,等待确认,然后再重复这个过程。
  • 发送方会:等待接收方的消息,收到并处理完消息后发送确认,然后再重复这个过程。

你也可以反过来,发送方等待接收方来获取接下来的N条可用消息。

这些方法和其他流量控制可以通过函数来隐藏,第一个方法在gen_server:call/2,3中大部分已经可以使用了,针对的是gen_server的OTP行为进程。

我认为在Erlang中使用异步消息传递是更好的方法,因为当延迟很高时,你可能非常希望避免在计算机之间进行同步。这样你就可以设计一些聪明的方法来实现流量控制。比如,要求接收方对发送方发送的每N条消息进行确认,或者不时发送一个特殊的“收到这个消息后请给我发个信号”的消息,以便计算响应时间。

撰写回答