最近在看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初探的更多相关文章

  1. spring揭秘 读书笔记 一 IoC初探

    本文是王福强所著<<spring揭秘>>一书的读书笔记 ioc的基本概念 一个例子 我们看下面这个类,getAndPersistNews方法干了四件事 1 通过newsList ...

  2. 【驱动笔记9】初探IRP

    文章作者:grayfox作者主页:http://nokyo.blogbus.com原始出处:http://www.blogbus.com/nokyo-logs/34005738.html 此前我们可能 ...

  3. Linux内核笔记--网络子系统初探

    内核版本:linux-2.6.11 本文对Linux网络子系统的收发包的流程进行一个大致梳理,以流水账的形式记录从应用层write一个socket开始到这些数据被应用层read出来的这个过程中linu ...

  4. Spring笔记之(一)初探

    对spring框架的学习我是从模拟它的简单实现开始,这样也易于领悟到它的整个框架结构,以下是简单实现的代码: 配置文件:spring.xml <?xml version="1.0&qu ...

  5. angular2 学习笔记 ( 4.0 初探 )

    目前是 4.0.0-rc.2. 刚好有个小项目要开发,就直接拿它来试水啦. 更新 cli 到最新版, 创建项目, 然后 follow https://github.com/angular/angula ...

  6. Python之路【第二十三篇】:Django 初探--Django的开发服务器及创建数据库(笔记)

    Django 初探--Django的开发服务器及创建数据库(笔记) 1.Django的开发服务器 Django框架中包含一些轻量级的web应用服务器,开发web项目时不需再对其配置服务器,Django ...

  7. Python源代码剖析笔记3-Python运行原理初探

    Python源代码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源代码剖析笔记,然而慢慢觉得没有从一个宏观 ...

  8. 深度学习课程笔记(十一)初探 Capsule Network

    深度学习课程笔记(十一)初探 Capsule Network  2018-02-01  15:58:52 一.先列出几个不错的 reference: 1. https://medium.com/ai% ...

  9. 初探C++运算符重载学习笔记&lt;2&gt; 重载为友元函数

    初探C++运算符重载学习笔记 在上面那篇博客中,写了将运算符重载为普通函数或类的成员函数这两种情况. 以下的两种情况发生.则我们须要将运算符重载为类的友元函数 <1>成员函数不能满足要求 ...

随机推荐

  1. CentOS 7 使用iptables 开放端口

    CentOS 7.0默认使用的是firewall作为防火墙,这里改为iptables防火墙. 1.关闭firewall: systemctl stop firewalld.service system ...

  2. HDU 5667 Sequence【矩阵快速幂+费马小定理】

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5667 题意: Lcomyn 是个很厉害的选手,除了喜欢写17kb+的代码题,偶尔还会写数学题.他找到 ...

  3. Oracle 实现 一个关键字 匹配多个 字段

    有这么一个需求,满足只有一个输入框的条件下,支持不同数据列的搜索结果. 说白了,就是这个 输入框  既可以用来 搜索姓名,也可以搜索 年龄,地址等. 分析: 一般情况下,我们的一个输入框对应 数据库 ...

  4. Unity3D 对象池思想 在游戏开发中的运用

    分类:U3D 1.在王者荣耀中,每30秒小兵会出现一波,出现之后会被敌方玩家或敌方小兵销毁,一局游戏下来,小兵会被创建多次,同时也会被销毁,在游戏中,这种频繁的创建和销毁游戏对象是很损耗性能的.在游戏 ...

  5. kafka基础介绍

    kafka基础介绍 一.kafka介绍 1.1主要功能 根据官网的介绍,kafka是一个分布式流媒体的平台,它主要有三大功能: 1.11:It lets you publish and subscri ...

  6. 用systemtap来做系统性能分析和事件分析

    http://zhengheng.me/2015/02/11/systemtap-analy/

  7. PYTHON 源码

    http://www.wklken.me/index2.html http://blog.csdn.net/dbzhang800/article/details/6683440

  8. poj 1659 Frogs&#39; Neighborhood 度序列可图化 贪心

    题意: 对一个无向图给出一个度序列,问他是否可简单图化. 分析: 依据Havel定理,直接贪心就可以. 代码: //poj 1659 //sep9 #include <iostream> ...

  9. struts开发&lt;在eclipse中配置struts. 一&gt;

    1.获取struts的jar包 1.1首先在http://struts.apache.org/download.cgi#struts23163这里下载 struts的文件包(选择struts-2.3. ...

  10. 【转载】C#中回滚TransactionScope的使用方法和原理

    TransactionScope只要一个操作失败,它会自动回滚,Complete表示事务完成 实事上,一个错误的理解就是Complete()方法是提交事务的,这是错误的,事实上,它的作用的表示本事务完 ...