如何使一个演员同时做两件事?

时间:2019-03-01 03:21:36

标签: python parallel-processing ray

我定义了learnerworker。我希望learner在后​​台运行其成员函数learn,并且偶尔workerlearner发送一些信息进行打印。

以下代码为示例

import ray

@ray.remote
class Learner():
    def __init__(self):
        pass

    def learn(self):
        while True:
            pass # do something, such as updating network 

    def log_score(self, score):
        print('worker', score)

@ray.remote
class Worker():
    def __init__(self, learner):
        self.learner = learner

    def sample(self):
        for i in range(1000000):
            if i % 1000 == 0:
                self.learner.log_score.remote(i)

ray.init()

learner = Learner.remote()
worker = Worker.remote(learner)


worker.sample.remote()
learner.learn.remote()

while True:
    pass

但是,learnerlog_score完成之前不会运行learn,这不是我想要的。我想过一种使它起作用的方法:我没有Learner.learn调用它,而是显式调用了它。具体来说,我重新定义Workerlearn如下

sample

虽然这可行,但是现在我必须控制"""Learner""" def learn(self): # no loop here pass # do something, such as updating network """Worker""" def sample(self): for i in range(1000000): if i % 1000 == 0: self.learner.learn.remote() self.learner.log_score.remote(i) 的调用频率,这似乎有点多余。有什么更好的方法可以实现我想要的?

1 个答案:

答案 0 :(得分:3)

这是一个很好的问题。在Ray的actor模型中,每个actor任务都是原子性的,因为actor会一次执行任务,直到上一个任务返回之前,才开始新任务。这种选择简化了有关并发的推理,但使角色一次执行两件事变得更加困难。

要进行类似的工作,您实际上有两种选择。

  1. 线程处理:让actor在后台线程中做一些工作,并使actor的主线程保持空闲状态,以便它可以执行新任务。

    import ray
    import threading
    import time
    
    @ray.remote
    class Actor(object):
        def __init__(self):
            self.value = 0
            self.t = threading.Thread(target=self.update, args=())
            self.t.start()
    
        def update(self):
            while True:
                time.sleep(0.01)
                self.value += 1
    
        def get_value(self):
            return self.value
    
    ray.init()
    
    # Create the actor. This will start a long-running thread in the background
    # that updates the value.
    a = Actor.remote()
    
    # Get the value a couple times.
    print(ray.get(a.get_value.remote()))
    print(ray.get(a.get_value.remote()))
    
  2. 更小的工作单元::这意味着对代码进行重组,以使actor方法不会永远循环。在您的示例中,您可以使learn函数在循环经过一定次数后返回。在这种情况下,必须连续提交新的learn任务。甚至可以让learn方法提交return并提交自身,以允许在两者之间安排其他方法。有多种方法可以执行此操作,具体取决于您的应用程序,但下面是一个示例。

    import ray
    import threading
    import time
    
    @ray.remote
    class Actor(object):
        def __init__(self):
            self.value = 0
    
        def set_handle_to_self(self, handle_to_self):
            self.handle_to_self = handle_to_self
    
        def learn(self):
            for _ in range(10):
                time.sleep(0.01)
                self.value += 1
    
            # Submit the learn task again so that the learning continues
            # but other methods can be scheduled in between.
            self.handle_to_self.learn.remote()
    
        def get_value(self):
            return self.value
    
    ray.init()
    
    # Create the actor. This will start a long-running thread in the background
    # that updates the value.
    a = Actor.remote()
    # Give the actor a handle to itself so that it can submit tasks to itself.
    a.set_handle_to_self.remote(a)
    
    # Start the learning, which will continue forever.
    a.learn.remote()
    
    # Get the value a couple times.
    print(ray.get(a.get_value.remote()))
    print(ray.get(a.get_value.remote()))