1. Actors
Ray中的远程函数被认为是功能性强和副作用低的框架。 仅限于远程函数的情况下,可以为我们提供分布式函数编程,这对于许多使用情况非常有用,但在实践中会受到一些限制。
Ray通过actor扩展了数据流模型。 一个actor本质上是一个有状态的worker(或service)。 当一个新的actor被实例化时,一个新的worker被创建,并且该actor的方法被安排在该特定的worker上,并且可以访问和改变该worker的状态。
假设我们已经开始了Ray。
import ray
ray.init()
2. 定义和创建一个actor
考虑下面的简单例子。装饰器 ray.remote表明类Counter实例化后为actor。
@ray.remote
class Counter(object):
def __init__(self):
self.value = 0
def increment(self):
self.value += 1
return self.value
为了真正创建一个actor,我们通过调用Counter.remote()来实例化该类。
a1 = Counter.remote()
a2 = Counter.remote()
当一个actor被实例化,下面的情况发生
- 选择集群中的一个节点,并在该节点上(由该节点上的本地调度程序)创建一个worker,以便运行在该actor上调用的方法。
- 在该worker上创建一个Counter对象,并运行Counter构造函数。
3. 使用actor
我们可以通过调用actor的方法来调度它的任务。
a1.increment.remote() # ray.get returns 1
a2.increment.remote() # ray.get returns 1
当a1.increment.remote()被调用,下面的情况发生。
- 任务被创建。
- 该任务被直接分配给负责该actor的本地调度程序(由驱动程序的本地调度器控制)。 因此,此调度过程绕过全局调度程序。
- 一个对象ID被返回。
然后,我们可以调用ray.get根据对象ID来获取实际值。
同样,对a2.increment.remote()的调用将产生一个任务,该任务被调度到第二个Counter actor上。 由于这两个任务在不同的actor上运行,它们可以并行执行(请注意,只有该actor上的方法才被调度分配到actor worker上,常规的远程函数不会)。
另一方面,在同一个Counter actor上调用的方法按照它们被调用的顺序依次执行。 因此它们可以相互共享状态,如下所示。