我定义了learner
和worker
。我希望learner
在后台运行其成员函数learn
,并且偶尔worker
向learner
发送一些信息进行打印。
以下代码为示例
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
但是,learner
在log_score
完成之前不会运行learn
,这不是我想要的。我想过一种使它起作用的方法:我没有Learner.learn
调用它,而是显式调用了它。具体来说,我重新定义Worker
和learn
如下
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)
的调用频率,这似乎有点多余。有什么更好的方法可以实现我想要的?
答案 0 :(得分:3)
这是一个很好的问题。在Ray的actor模型中,每个actor任务都是原子性的,因为actor会一次执行任务,直到上一个任务返回之前,才开始新任务。这种选择简化了有关并发的推理,但使角色一次执行两件事变得更加困难。
要进行类似的工作,您实际上有两种选择。
线程处理:让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()))
更小的工作单元::这意味着对代码进行重组,以使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()))