最近在看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并执行流程。

最新文章

  1. libcpmt.lib 与 msvcprt.lib
  2. php删除目录下的所有文件和目录
  3. 外网不能访问部署在虚机的NodeJs网站(80端口)
  4. Python自动化 【第九篇】:Python基础-线程、进程及python GIL全局解释器锁
  5. Oracle找出非数字
  6. VC++NMAKE
  7. VS合集/6.0/2005/2008/2010/2012/2013 绿色版精简版
  8. js事件流、事件处理程序/事件侦听器
  9. 小学生之深入C#
  10. Java基础知识强化44:StringBuffer类之把数组拼接成指定格式的字符串的案例
  11. jenkins 集成 redmine 账户验证的方案
  12. 【UWP】FFmpeg库的编译
  13. JVM菜鸟进阶高手之路二(JVM的重要性,Xmn是跟请求量有关。)
  14. Tornado异步
  15. CSS3关于过渡效果的问题
  16. nginx静态服务器配置
  17. Android 启动APP时黑屏白屏的解决方案
  18. Java RSA 公钥加密私钥解密
  19. TZOJ:3660: 家庭关系
  20. 安装java时,配置环境变量classpath的作用

热门文章

  1. Python入门--11--自定义函数
  2. 关于几种UI框架简单总结
  3. kafka exactly-once
  4. POJ 题目3450 Corporate Identity(KMP 暴力)
  5. Java中的Copy-on-Write容器 &amp; ConcurrentHashMap &amp; HashTable比较
  6. linux 源码编译安装apache
  7. CentOS7 docker.repo 用阿里云Docker Yum源
  8. CentOS安装Openfire服务
  9. 线程池实例:使用Executors和ThreadPoolExecutor
  10. C#语言基础语句