如果我们认为演员是人,那么信息就像…信息。
假设一个老板想要一个数字列表的平方根
a
也不想做所有的计算。他可以雇佣一些工人,老板会知道他们的电话号码。
所以老板会给每个工人发短信,告诉他们“找到
a_i
;完成后,请拨打555-1234回复我。”。此说明是
消息
. 然后,老板将等待工人们完成工作。
+------+ sqrt(i=0, a_i=9) +------------+
| Boss | ------------------------> | Worker 0 |
+------+ +------------+
| sqrt(i=1, a_i=16) +------------+
â--------------------------> | Worker 1 |
+------------+
....
工人们完成计算后,会给老板发一条短信,并报告结果。这也是在消息传递中完成的。
+------+ set_result(i=0, val=3) +------------+
| Boss | <------------------------ | Worker 0 |
+------+ +------------+
^ set_result(i=1, val=4) +------------+
â--------------------------- | Worker 1 |
+------------+
....
这听起来像是面向对象的编程,但是当消息被发送或接收时,它们没有被传递到的顺序。
异步地
. (但是,在参与者本身中,消息是同步接收和排队的。)
当用代码写的时候,它可能是
actor Boss:
receive('run'):
worker_addrs = spawn_many(SqrtWorker, len(a)) # hire workers.
for i, addr in enumerate(worker_addrs):
send(addr, 'sqrt', reply_addr=self, i=i, a_i=a[i])
receive('set_value', i, val):
a[i] = val
actor SqrtWorker:
receive('sqrt', reply_addr, i, a_i):
send(reply_addr, 'set_value', i, sqrt(a_i))
quit()
没有“共享状态问题”,因为没有复制就无法共享状态。在上面的示例中,list的元素
一
是
复制的
给每个工人。事实上,只有老板知道
一
这是一个
局部状态
.
如果我们真的想
一
共享?在演员模型中,我们将把他们转换成一个新的演员,并将这个演员的电话号码发送给工人。
+------+ sqrt(i=0, a_phoneNum=555-1111) +----------+
| Boss | -------------------------------> | Worker 0 |
+------+ +----------+
+---+
| a |
+---+
然后,工作人员向列表参与者询问所需的信息(可能是因为老板已经给出了
一
给工人。)
+------+ +----------+
| Boss | | Worker 0 |
+------+ +----------+
|
+---+ |
| a | <---------------------------â
+---+ get(i=0)
过了一段时间列表回复…
+------+ +----------+
| Boss | | Worker 0 |
+------+ +----------+
^
+---+ list_val(i=0, val=9) |
| a | ----------------------------â
+---+
然后工作人员可以在收到消息后计算平方根。
list_val
.
+------+ set_result(i=0, val=3) +----------+
| Boss | <------------------------------ | Worker 0 |
+------+ +----------+
+---+
| a |
+---+
老板最终更新了共享状态
+------+ +----------+
| Boss | | Worker 0 |
+------+ +----------+
| set(i=0, val=3)
| +---+
â------> | a |
+---+
访问这样的共享状态会有什么问题吗?由于收到的消息
一
必须同步运行,所有读/写操作都会相互干扰。因此,没有必要乱用互斥体。