LabVIEW的ActorFramework笔记
1 前置知识储备
自分布式计算出现以来,业界已经开始广泛研究基于消息传递编程模型的解决方案。关于消息传递,Wikipedia 描述其广泛定义主要包括:远程过程调用(Remote Procedure Calls, RPC) 和 消息传递接口(Message Passing Interface, MPI)。但是,如今我们所谈到的消息传递,通常是指 actor 模型(Actor Model)。作为一种通用的消息传递编程模型,其起源于 20 世纪 70 年代,如今被广泛用于构建大规模可伸缩分布式系统。
Actor 模型
一个 actor 定义为一个计算单元。所谓麻雀虽小,五脏俱全,每个 Actor 包含了存储、通信、计算等能力。在分布式系统中,通常包含了非常多的服务器集群,每一台服务器又包含了大量 actor 实例,它们共同构成了强大的并行计算能力。
Actor 的核心思想是 独立维护隔离状态,并基于消息传递实现异步通信。围绕其进行实现,actor 通常包含以下特征:
每个 actor 持有一个邮箱(mailbox),本质上是一个队列,用于存储消息。
每个 actor 可以发送消息至任何 actor。
每个 actor 可以通过处理消息来更新内部状态,对于外部而言,actor 的状态是隔离的状态(isolated state)。
为了便于通信,actor 模型使用 异步 消息传递。消息传递不使用任何中间实体,如:通道(channel)。由于 actor 模型的消息是异步传递的,中间可能会经过很长时间,甚至丢失,因此无法保证消息到达目标 actor 时的顺序。每个 actor 都完全独立于任何其他实例,actor 之间的交互完全基于异步消息,因此能够在很大程度上避免共享内存的存在问题。
任务调度
Actor 模型根据任务调度的方式可以分为两种,分别是:
(1)基于线程(thread-based)的 actor 模型
基于线程的 actor 模型,其本质是为每一个 actor 分配一个独立的“线程”。这里的“线程”并不是严格意义的操作系统线程,而是广泛意义的执行过程,它可以是线程、协程或虚拟机线程。
在基于线程的 actor 模型中,每个 actor 独占一个线程,如果当前 actor 的邮箱为空,actor 会阻塞当前线程,等待接收新的消息。在实现中,一般使用 receive 原语。
这种 actor 模型实现起来比较简单,但是缺点也非常明显,由于线程数量受到系统的限制,因此 actor 的数量也会受到限制。现阶段,只有少部分 actor 模型采用基于线程的实现方式,如:Erlang、Scala Actor、Cloud Haskell。
(2)事件驱动(event-driven)的 actor 模型
在事件驱动的 actor 模型,actor 并不直接与线程耦合,只有在事件触发(即接收消息)时,才为 actor 的任务分配线程并执行。这种方式使用续体闭包(Continuation Closure)来封装 actor 及其状态。当事件处理完毕,即退出线程。通过这种方式,我们可以使用很少的线程来执行大量 actor 产生的任务。在实现中,一般使用 react 原语。
事件驱动的 actor 模型在消息触发时,会自动创建并分配线程。在这种过程中,一般的优化是将 actor 执行建立在底层的线程池之上,这些线程可以是线程、协程或虚拟机线程。从概念上讲,这种实现与 run loop、event loop 机制非常相似。
2 LabVIEW的AF示例
假设有学生和老师这两个actor,学生有个“向老师交了一份儿作业”的方法;老师有“批改作业”这个方法;
基于ActorFramework的思路便是:
2.1先来设计学生这个actor:
(1)新建一个空白的Project,并命名保存为“AF_student_teacher.lvproj”:
(2)右键新建操作者Actor——Student:
student这个类我们还定义一个“姓名”的私有私有(私有字段);并写一个其“写入”的方法:
(3)新建 静态模板方法——交作业
“交作业”方法体代码:
(4)为“交作业”方法创建 消息:
(5)测试“Student”Actor:
点击vi运行按钮:
至此,第一个Actor ——student设计完成。
2.2 设计Teacher这个Actor
同理,右键新建actor——Teacher
老师的“批改作业”方法(静态模版方法):
创建教师的“批改作业”方法消息队列:
也来写一个测试教师Actor的vi:
至此,Teacher这个Actor我们也设计完了。
2.3 两个个Actor之间进行交互
我们的场景是:
学生“交作业”,然后老师收到了作业进行“批改作业”。
很显然,student这个Actor想要发送一条消息给Teacher这个Actor;这里,Student就需要知道Teacher的邮箱地址——消息队列;
我们修改student类的私有数据,增加Teacher的消息队列。
同时右键新建给此私有字段开发一个写入接口:
我们在学生“交作业”的同时给Teacher这个Actor通过其消息队列撒送一条信息;所以修改的student的“交作业”方法如下:
然后我们写一个test total.vi来测试看看:
需要注意的地方:
(1)每个actor最后在使用完时,都要发送“
”让其停止,否则,你会发现其vi尚在后台运行,就不可再编辑了。
(2)每个actor没用完之前如果你关闭了,则这个actor就停止运行而不会响应收到的消息。
(3)从这里可以看出,一个Actor无论是要调用其他Actor的方法,只需要获取到其方法对应的消息队列即可。
为了更好的演示各个操作者是相互独立运行的的效果,我们这里再把Teacher的“批改作业”方法稍微修改一下:
最后test Total.vi运行的结果:
参考资料:
http://chuquan.me/2023/01/15/actor/
https://www.cnblogs.com/EltonLiang/p/6838895.html