使用一个Actor池有意义吗?

8

我正在学习并喜欢上Actor模式。目前我正在使用Scala,但我对该架构风格在Scala、Erlang、Groovy等语言中的应用也很感兴趣。

我考虑的情况是需要并发执行任务,比如说“运行作业”。

使用线程池和阻塞队列,每个线程轮询阻塞队列,并根据队列中的作业进行处理。

那么使用Actor,最佳方式是什么呢?是否有必要创建一个Actor池,并向其中发送包含作业的消息?或者使用一个“协调器”Actor?

注意:我忘记提到的一点是:如果我想限制应用程序同时处理的作业数量怎么办?可以使用配置设置吗?我认为池可能会使这变得容易。

谢谢!


1
是的,限制在任务列表上工作的演员数量是有意义的。至少Erlang提供了足够的并发性,通过使用它们生成足够的进程,您可以耗尽大多数系统资源。 - Christian
我已经尝试用更加专注、具体和明确的方式在这里重新表述了这个问题:https://dev59.com/y3E95IYBdhLWcg3wbtbO - Avi Flax
3个回答

5

当创建和销毁资源的成本较高时,池(Pool)是一种机制。在Erlang中,这种情况并不适用,因此您不应该维护一个池。

您应该根据需要生成进程,并在使用完毕后销毁它们。


谢谢,但是如果我想限制我的应用程序同时处理的作业数量怎么办?也许可以通过配置设置来实现?我在考虑使用池来轻松完成这个任务。 - Avi Flax
3
@Avi: 我认为你需要在这里做出区分。 "pool"通常指的是(至少对于我来说)实际上保留演员/进程并重复使用它们。在 Erlang 中,您不需要这样做,可以将它们丢弃并产生新的。当然,您可以实现一个“全局计数器”(以服务器进程、ets表等形式),在产生另一个作业进程之前进行轮询。实际上,您需要一些类似这样的功能来实现负载控制... - Zed
@Zed: 说得好,谢谢。也许我应该问一下,“如何以一种好的方式限制使用actors的并发性?” - Avi Flax
1
你可以使用Actor.mailboxSize来获取你所寻找的约束类型。 - marc esher
1
这种方法似乎也是实现您所寻求的方式之一:https://dev59.com/pHNA5IYBdhLWcg3wVcJx - marc esher

4
有时候,在处理大任务列表时,限制同时运行的工作进程数量是有意义的,因为生成进程以完成任务涉及资源分配。至少进程会使用内存,但它们也可能保持打开的文件和/或套接字,这些通常仅限于数千个,一旦用完就会失败且不可预测。
要拥有一个拉动式的任务池,可以生成N个链接进程,它们请求任务,并将一个函数传递给它们进行监控。一旦监视进程结束,它们就会回来获取下一个任务。具体需求驱动细节,但这是一种方法的概述。
我让每个任务生成一个新进程的原因是,进程确实具有某些状态,而从干净的状态开始很好。将进程的最小堆大小设置为调整其生命周期中所需GC数量的最小值是一种常见的微调。释放进程的所有内存并在下一个任务上启动一个新的进程也是非常有效的垃圾收集。
像这样使用两倍数量的进程感觉奇怪吗?这是您需要在Erlang编程中克服的感觉。

非常有趣,谢谢!我可能会将其标记为“已接受”的答案,我需要考虑一下。 - Avi Flax

2
在所有情况下,都没有最佳的方法。决定取决于作业数量、持续时间、到达时间和所需完成时间。
仅生成演员和使用池之间最明显的区别在于,在前者的情况下,您的作业将几乎同时完成,而在后者的情况下,完成时间将在时间上分布。虽然平均完成时间相同。
使用演员的优点是编码简单,因为它不需要额外的处理。权衡是您的演员将竞争您的CPU核心。无论使用什么编程范例,您都不能拥有更多的并行作业,超过CPU核心(或HT)的数量。
例如,假设您需要执行100,000个作业,每个作业需要一分钟,结果将于下个月到期。你有四个核心。你会生成100,000个演员,每个演员一个月内争夺资源,还是只排队,每次执行四个?
作为反例,想象一下在同一台机器上运行的Web服务器。如果您有五个请求,您希望在T时间内为四个用户提供服务,并在2T时间内为一个用户提供服务,还是在1.2T时间内为所有五个用户提供服务?

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