前提:有多线程知识
我对这种模式有点疑问,思维转不过来,于是记录如下:
=====================================
Actor 每个是独立的对象。
Actor 之间通过消息通信。
"actor之间的通信是单向和异步的。因此,消息发送者不知道消息是什么时候被发送, 也不会接收到一个消息已被处理的回应或通知。"
Actor 每个应该有一个消息队列(邮箱),队列的操作需要线程安全。
使用者不需要关心锁,Actor 模式是“简单”的。
上面链接的示例代码不够明朗,精简后:
from queue import Queue
from threading import Thread
import time
class Actor:
def __init__(self):
self._mailbox = Queue()
def send(self, msg):
self._mailbox.put(msg)
def _recv(self):
return self._mailbox.get()
def start(self):
t = Thread(target=self._run)
t.daemon = True
t.start()
def stop(self):
p.send('Exit')
def _run(self):
while True:
msg = self._recv()
print('Got:', msg)
if msg is 'Exit':
break
# Sample use
p = Actor()
p.start()
p.send('Hello')
p.send('World')
p.stop()
p.send('Hello')
# 方便测试
time.sleep(1)
actor 最少只需要一个 send 方法。
其他方法一般通过调用 send 封装对外,方便使用,不是必要的。
只要 send 是线程安全的, 消息处理是 actor 自己内部处理,没线程同步问题。
胜在简单安全。
Actor 应该有个标识/地址。
actor 可以在远程主机上,需要一个逻辑上的地址标识。
个人感觉,一般用不上。
阻塞
外部线程调用 actor send 不会阻塞,但 actor 内部(循环)可能会阻塞。
按照一般的过程思维,既然异步,怎么获得处理结果?TODO:
没找到明朗方案,个人认为:
合适的方案:处理结果发给某个 Actor。
不适合的方案:实现 result 方法并调用(查询),感觉模型变味了,万一 actor 内部阻塞了就影响外部了。
其他
使用 Actor 模型比较著名的是 Erlang 。
会联想到微服务+RPC。
个人认为 Actor 模式测试和调试也方便,通过 Actor 地址发送消息去测试。
对比 CSP 模型
CSP 的 chanel 类似 Actor 的 mailbox,但是独立出来;
chanel 缓冲区为0或有限个,发送消息后,如果接收方一直不接收,会阻塞(即使有缓冲区也会用完);而Actor 邮箱容量是无限的(理论上)。
golang 例子
import (
"time"
)
func worker1(limit int, c chan int) {
for i := 0; i < limit; i++ {
x :=
fmt.Println("worker1",x,i)
time.Sleep(300*1000*1000)
c
}
// close(c)}
func worker2(limit int, c chan int) {
for i := 0; i < limit; i++ {
x :=
fmt.Println("worker2",x,i)
time.Sleep(300*1000*1000)
c
}
// close(c)}
func test28() {
c := make(chan int)
go worker1(5, c)
go worker2(5, c)
c
time.Sleep(3000*1000*1000)
}