actor是一种异步并发处理模型。最具代表性的是erlang语言。
在golang中,最具代表性的并发模式为csp,多协程并发。
这两者的区别更多类似于网络并发。 网络程序设计中的并发复杂性 这篇文章中有介绍,关于事件驱动和多线程并发。其两者都有着各自的优缺点。
protoactor-go的源码地址为:
https://github.com/AsynkronIT/protoactor-go
对于一个大型的项目来讲,我个人的学习习惯于从最小版本开始学起。这是因为,在一个项目最初的时候,大体功能和架构都已经成形,最初的版本,一般来说,代码量都较少,功能集最小。学习曲线低,并且又最初版本,慢慢往高版本过渡,也能更了解项目进化的过程,也是一个学习的过程。
并且在实际使用过程中,大多数情况下,我们可能不需要那么多的功能集,并且需要根据实际情况做一些二次开发,此时的话,也许低版本的会更贴近实际使用场景和二次开发场景。
基本的模块划分,有最初版本的源码分析:
开源代码protoactor-go[e866f39]源码分析
下面分析2661d6a版本
此版本相对于上一篇文章 开源代码protoactor-go[e866f39]源码分析 来说,增加了一个异步调度。其余地方都是一样的。
/github.com/AsynkronIT/protoactor-go/main.go
流程图中发生变化的地方:
在ChannelActorRef的Tell,SendSystemMessage,中增加了调用schedule
ActorzOf,现在只承担初始化操作,并没有调度的功能。
上面是两对变量,用于原子变量操作。
64:这里的调度,会先通过原子操作CompareAndSwapInt32,来做同步的判断,判断schedulerStatus是否为Idle,是则swap成Busy,否则不swap。这个是常用的原子同步操作
65:因为有人调用schedule,说明是有msg需要传递。这里将hasMoreMessages设置成MailboxHasMoreMessages
66:判断是否有swap,防止重入
67:开启processMessages协程
处理msg协程
72:防止重入,将hasMoreMessages设置空(MailBoxHasNoMessages)
73-86:一个重复30次的for循环,尝试30次的操作,进行msg的chan监听,并调用相应的处理接口。
87:操作完,将schedulerStatus设置为空(MailboxIdle )
88-95:属于double check的作用,防止在86-87行代码之间有人调用schedule而被忽略掉。这个是关键点。
88:读取hasMoreMessages信息,看看是否有没有处理的信息
89:读取scheduleStatus信息
90:判断是否有未读取信息,并判断status是否为空(MailboxIdle)
91:尝试将schedulerStatus状态进行CompareAndSwap操作
93:重启一个新的processMessages协程。
这里的异步调度操作,利用了原子操作来实现同步,并采用double check方式来防止信息忽略。这个调度处理是很值得学习的。可以用到很多地方。
那么我们再看看最新的版本里调度是如何的?
代码最新更新是2019年4月29日 GMT+8 下午11:29:31
代码目录:
从这里也可以看到随着版本的升级,相对比最初版本,代码复杂度越来越高,模块抽象越来越多,当然功能也越来越多。
但原理其实没差。
我们看看最新版本的调度
github.com/AsynkronIT/protoactor-go/mailbox/mailbox.go
模块做了抽象,上面的
MessagesInvoker:消息真正处理的地方
Mailbox:消息调度的地方
有一个default的Mailbox,暂时还没有看到其他的Mailbox。那么就以这个来分析
在看之前,我们先看下dispatcher
github.com/AsynkronIT/protoactor-go/mailbox/dispatcher.go
这里有异步和同步两个dispatcher
goroutineDispatcher:异步的,在Schedule中,开启了协程
synchronizeDispatcher:同步的,在Schedule中,是同步执行
这两个type都是int,在throughput返回的其int值,这个将在调度中有作用
回到mailbox中
注册接口:消息处理接口,Dispatcher接口(异步和同步)
两个消息发送:
1、postUserMessage,发送user message
2、postSystemMessage,发送system message
从处理流程上,和最初的调度版本来看,流程是一样的。都在消息发送中,调用了schedule
这里依旧用的原子操作来进行同步,对schedulerStatus进行CompareAndSwapInt32操作。
然后就进如到Dispatcher中的Schedule,这个取决于Dispatcher初始化是异步还是同步,提供了goroutineDispatcher和synchronizeDispatcher供选择。
85:真正的处理,封装到run中了。
88:将SchedulerStatus设置为空(idle)
89:加载sysmessages数量
90:加载usermessages数量
92:判断是否有消息还未处理
94:重新check schedulerStatus,这里就是double check
96:若还有未处理的,重新进入run
整个处理流程是一模一样的,并没有发生变化
看下run中
115:设置了i为0,t为Dispatcher的值。这里是与之前调度不一样的地方
117-120:当i>t时候,调用了runtime.Gosched()。此处是与最初版本不一样的地方。增加这个的好处是,当处理的消息数量过多时,这个for循环是不停的运行。增加了Gosched的话,是当消息处理一定数量的时候,让其停止运行,等待runtime重新调度其运行。这样可以防止cpu资源一直被消息处理占用。
125-139:systemMessage的处理地方
146-152:userMessage的处理地方
从最初版本,与最新版本的调度对比来看。其原理几乎没有发生变化。也验证了,对于开源代码的学习来说,学习原理的话,并非是最高版本是好。反而低版本对学习原理来说,学习上手更快。
龚浩华
月牙寂道长
qq:29185807
2019年05月15日
如果你觉得本文对你有帮助,可以转到你的朋友圈,让更多人一起学习。
第一时间获取文章,可以关注本人公众号:月牙寂道长,也可以扫码关注
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- libmodbus源码分析(3)从机(服务端)功能源码分析
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Clean Code
Robert C. Martin / Prentice Hall / 2008-8-11 / USD 49.99
Even bad code can function. But if code isn’t clean, it can bring a development organization to its knees. Every year, countless hours and significant resources are lost because of poorly written code......一起来看看 《Clean Code》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
HSV CMYK 转换工具
HSV CMYK互换工具