BlueSea笔记<1>--Cricket初探
最近在看Cricket这个实现了Actor模式的F#开源框架,对其工作方式作了一番探究。首先来看一段简单的例子代码:
type Say = | Hello
let greeter =
actor {
name "greeter"
body (
let rec loop() = messageHandler {
let! msg = Message.receive()
match msg with
| Hello -> printfn "Hello" return! loop()
}
loop())
} |> Actor.spawn
先是定义了消息类型Say,接着通过computation-expression(计算表达式)的方式定义了greeter这个actor。actor计算表达式的定义见ActorConfigurationBuilder,包括body、name等都通过CustomOperationAttribute的方式给出了定义。比如body:
member __.Body(ctx, behaviour) =
{ ctx with Behaviour = behaviour }
这里的behaviour即是上述例子中"body语法字"括号中的代码块。它构建了新的ctx:ActorConfiguration<'a>,即这里的ActorConfiguration<Say>。可以预见,behaviour作为一个被缓存的行为,必定在将来一个合适的时机被调度执行。在这之前,还是先看下代码块中的具体执行内容。
messageHandler又是一个计算表达式,定义在MessageHandlerBuilder中。这里主要是看下let!与return!的定义。先看let!:
member __.Bind(MH handler, f) =
MH (fun context ->
async {
let! comp = handler context
let (MH nextComp) = f comp
return! nextComp context
}
)
member __.Bind(a:Async<_>, f) =
MH (fun context ->
async {
let! comp = a
let (MH nextComp) = f comp
return! nextComp context
}
)
这里MH的定义为:type MessageHandler<'a, 'b> = MH of ('a -> Async<'b>)。这里需要反复强调的是,async声明只是被转换为Async.Bind()这种形式的函数调用,并不代表任何对象。至于返回Async<'b>,那是因为Async.Bind()函数本身返回Async<'b>对象,即AsyncBuilder加工处理的中间对象。不止async,任何计算表达式都是如此。
Message.receive()的定义为:
let receive() = MH (fun (ctx:ActorCell<_>) -> async {
let! msg = ctx.Mailbox.Receive()
ctx.Sender <- msg.Sender
ctx.ParentId <- msg.Id
ctx.SpanId <- Random.randomLong()
traceReceive ctx
return msg.Message
})
由Bind的定义可以看到,它包装了参数handler并返回新的MH-handler。我开始一直认为,Bind函数中会解析从Message.receive()的返回值,并交给后续代码块处理。但是这里却是返回了一个新的MH-handler,令人百思不得其解。事实上这依然是一个缓存的行为。我们可以把代码展开:
let rec loop() =
// Message.receive()返回的MH-handler
let msgHandlerReceive = MH (fun (ctx:ActorCell<_>) -> async {
let! msg = ctx.Mailbox.Receive()
ctx.Sender <- msg.Sender
ctx.ParentId <- msg.Id
ctx.SpanId <- Random.randomLong()
traceReceive ctx
return msg.Message
})
// 匹配msg并处理的代码块
let funCodeBlock = fun (msg:Say) ->
match msg with
| Hello -> printfn "Hello" let MH(leftCodeBlock) = loop()
// return! loop() => MessageHandler.ReturnFrom(loop())
MH(fun ctx ->
traceHandled ctx;
leftCodeBlock(ctx)) // let!中的处理,返回新的MH-handler(粘合receive和codeBlock,而codeBlock中会通过return!返回新的MH-handler由Async异步递归处理)
MessageHandler.Bind(msgHandlerReceive, fun codeBlock ->
MH (fun context ->
async {
let! comp = msgHandlerReceive context
let (MH nextComp) = codeBlock comp // 用户代码块返回新的MH-handler
return! nextComp context
}
) )
注意上述MessageHandler.Bind调用时传入的codeBlock即为funCodeBlock,也就是用户代码。这里可以清楚地看到loop()事实上是通过嵌套调用MessageHandler.Bind(各种do!和let!以及return!)构建返回了一个个新的MH-handler,将message接收、解析、用户代码处理等串联起来,当调用loop()时(也就是将来调用ActorConfiguration<Say>.Behaviour时)返回一个串联后的MH-handler,再在合适的时机加以执行。至此,整个流程已经清楚,剩下的就是搞清楚何时执行behaviour的问题了。在构建ActorConfiguration<Say>结束后将由Actor.spawn处理,会创建Actor对象,并在创建中通过Async.Start执行如下代码:
do! MessageHandler.toAsync ctx defn.Behaviour
这里defn.Behaviour即是当初串联而来的MH-handler,ctx即为ActorCell<Say>。再看下MessageHandler.toAsync就一目了然了:
let toAsync ctx (MH handler) = handler ctx |> Async.Ignore
接收ActorCell<Say>对象ctx并执行流程。
BlueSea笔记<1>--Cricket初探的更多相关文章
- spring揭秘 读书笔记 一 IoC初探
本文是王福强所著<<spring揭秘>>一书的读书笔记 ioc的基本概念 一个例子 我们看下面这个类,getAndPersistNews方法干了四件事 1 通过newsList ...
- 【驱动笔记9】初探IRP
文章作者:grayfox作者主页:http://nokyo.blogbus.com原始出处:http://www.blogbus.com/nokyo-logs/34005738.html 此前我们可能 ...
- Linux内核笔记--网络子系统初探
内核版本:linux-2.6.11 本文对Linux网络子系统的收发包的流程进行一个大致梳理,以流水账的形式记录从应用层write一个socket开始到这些数据被应用层read出来的这个过程中linu ...
- Spring笔记之(一)初探
对spring框架的学习我是从模拟它的简单实现开始,这样也易于领悟到它的整个框架结构,以下是简单实现的代码: 配置文件:spring.xml <?xml version="1.0&qu ...
- angular2 学习笔记 ( 4.0 初探 )
目前是 4.0.0-rc.2. 刚好有个小项目要开发,就直接拿它来试水啦. 更新 cli 到最新版, 创建项目, 然后 follow https://github.com/angular/angula ...
- Python之路【第二十三篇】:Django 初探--Django的开发服务器及创建数据库(笔记)
Django 初探--Django的开发服务器及创建数据库(笔记) 1.Django的开发服务器 Django框架中包含一些轻量级的web应用服务器,开发web项目时不需再对其配置服务器,Django ...
- Python源代码剖析笔记3-Python运行原理初探
Python源代码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源代码剖析笔记,然而慢慢觉得没有从一个宏观 ...
- 深度学习课程笔记(十一)初探 Capsule Network
深度学习课程笔记(十一)初探 Capsule Network 2018-02-01 15:58:52 一.先列出几个不错的 reference: 1. https://medium.com/ai% ...
- 初探C++运算符重载学习笔记<2> 重载为友元函数
初探C++运算符重载学习笔记 在上面那篇博客中,写了将运算符重载为普通函数或类的成员函数这两种情况. 以下的两种情况发生.则我们须要将运算符重载为类的友元函数 <1>成员函数不能满足要求 ...
随机推荐
- 【编码】封装RedisPubSub工具
基本介绍 核心原理:利用Redis的List列表实现,发布事件对应rpush,订阅事件对应lpop 问题一:Redis不是自带Pub/Sub吗? redis自带的pub/sub有两个问题: 1.如果发 ...
- spring data jpa 查询部分字段
@Query("select new map(ah as ah,salq as sqlq,yg as yg, bg as bg,ay as ay) FROM Aj where ahdm=?1 ...
- Executors
提供了工厂方法: Factory and utility methods for Executor, ExecutorService, ScheduledExecutorService, Thread ...
- ros使用罗技f710无线控制手柄
参考:blog.csdn.net/hcx25909/article/details/9042469 罗技F710无线控制手柄ROS下使用说明 安装手柄相关的包和驱动 sudo apt-get inst ...
- 51 NOD 1406 and query
我们知道一个数S会对所有它的子集S'产生1的贡献,但是我们直接枚举子集是 3^(log2 1000000)的,会炸掉:如果直接把每个有1的位变成0往下推也会凉掉,因为这样会有很多重复的. 但是我们发现 ...
- 注解@RequestMapping value 用法
本文引自:https://blog.csdn.net/qq_33811662/article/details/80864784 RequestMapping是一个用来处理请求地址映射的注解,可用于类. ...
- 2019年春招Android方向腾讯电话面试
第一问:TCP与UDP的区别 参考答案: 1.基于连接与无连接 2.TCP要求系统资源较多,UDP较少: 3.UDP程序结构较简单 4.流模式(TCP)与数据报模式(UDP); 5.TCP保证数据正确 ...
- nsmutablestring 属性声明为copy程序崩溃了
obj.mutableStr = (NSMutableString *)[[NSMutableString alloc] initWithString:@"Hello"]; NSL ...
- MBProgressHUD 显示方向异常
一直在iphone上使用MBProgressHUD做提示信息视图.一直都没有什么问题,但用在ipad上使用时.却有时会出现显示方向不正常.如ipad屏幕是横的,但当MBProgressHUD出现时却依 ...
- Android新技术学习——阿里巴巴免Root无侵入AOP框架Dexposed
阿里巴巴无线事业部近期开源的Android平台下的无侵入运行期AOP框架Dexposed,该框架基于AOP思想,支持经典的AOP使用场景.可应用于日志记录,性能统计,安全控制.事务处理.异常处理等方面 ...