Ray全局变量问题

Ray的远程函数功能remote应该被认为是功能性和无副作用的。仅限于远程函数限制我们使用分布式函数式编程,这对于许多用例来说都很好,但实际上有点受限。
Ray使用Actor扩展了数据流模型。Actor本质上是一个有状态的worker(或服务)

假设我们有多个任务在同一个actor上调用方法。例如,我们可能有一个Actor记录来自许多任务的执行信息。我们可以将actor句柄作为参数传递给相关任务来实现这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@ray.remote
class Actor(object):
def method(self):
pass

# 创建actor
actor = Actor.remote()

@ray.remote
def f(actor):
# 激活actor的函数
x_id = actor.method.remote()
# 真正的阻塞调用返回结果
return ray.get(x_id)

# 三个任务都会调用同一个actor的方法
f.remote(actor)
f.remote(actor)
f.remote(actor)

参考官方文档
https://docs.ray.io/en/latest/ray-core/patterns/global-variables.html#anti-pattern-using-global-variables-to-share-state-between-tasks-and-actors

全局变量共享是一种反模式的使用方法,不要使用全局变量与任务和参与者共享状态。相反,将全局变量封装在参与者中,并将参与者句柄传递给其他任务和参与者。

Ray 驱动程序、任务和 Actor 运行在不同的进程中,因此它们不共享相同的地址空间。这意味着,如果您在一个进程中修改全局变量,则更改不会反映在其他进程中

解决方案是使用Actor的实例变量来保存全局状态,并将参与者句柄传递到需要修改或访问状态的地方。

成功的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@ray.remote
class GlobalVarActor:
def __init__(self):
self.global_var = []

def set_global_var(self, var):
self.global_var.append()

def get_global_var(self):
return self.global_var


@ray.remote
class Actor:
def __init__(self, global_var_actor):
self.global_var_actor = global_var_actor

def f(self):
return ray.get(self.global_var_actor.get_global_var.remote()) + 3


global_var_actor = GlobalVarActor.remote()
actor = Actor.remote(global_var_actor)
ray.get(global_var_actor.set_global_var.remote(4))
# This returns 7 correctly.
assert ray.get(actor.f.remote()) == 7

失败的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import ray

ray.init()

global_var = 3


@ray.remote
class Actor:
def f(self):
return global_var + 3


actor = Actor.remote()
global_var = 4
# This returns 6, not 7. It is because the value change of global_var
# inside a driver is not reflected to the actor
# because they are running in different processes.
assert ray.get(actor.f.remote()) == 6

返回值为6,是因为Actor在定义时
角色方法会运行在一个*有状态(stateful)的工作进程上。实例化一个Actor时,会创建一个全新的worker,并且在该新的Actor上执行所有方法。所以当actor = Actor.remote()时,进程中的global_var值为3,即使后续修改了仍不会生效。

解决办法就是将Actor句柄作为参数传递给相关的任务即可实现全局数据共享。

保证Actor状态的一致性,对同一个Actor的方法调用是串行执行的。多个Actor,是并行地执行Actor的方法的