具有多个递归异步体的F#代理可以在每个中使用多个inbox.Receive()吗?

时间:2017-05-26 20:17:50

标签: f# agents mailboxprocessor

我这里有一个多状态F#MailboxProcessor示例,只是想知道为什么编译但行为是意外的 - F#代理只能在传入的lambda函数中有一个inbox.Receive()语句吗?我试图遵循" Expert F#3.0"中提供的一般示例模式。第284页,其中 使用多个异步{}实体允许多个状态,但是对于是否可以在每个异步中使用inbox.Receive()并不具体。

open System

let mb1<'T> = MailboxProcessor<string>.Start(fun inbox ->
                        let rec loop1 (n:int) = async {
                                    printfn "loop1 entry "
                                    let! msg = inbox.Receive()
                                    do! Async.Sleep(1000)
                                    printfn "loop1 calling loop2"  //msg received %A" msg
                                    return! loop2 (n+1) }

                        and loop2 (x:int) =     async {
                                    printfn "loop2 entry"
                                    let! msg2 = inbox.Receive()
                                    printfn "loop2 msg received %A" msg2
                                    printfn "loop2 calling loop1"
                                    return! loop1 (x+1) }
        loop2 0                    
                                        )

mb1.Post("data message 1")
mb1.Post("data message 2")

产量

loop2 entry
loop2 msg received "data message 1"
loop2 calling loop1
loop1 entry 
val it : unit = ()
> 
loop2 entry
loop2 msg received "data message 2"
loop2 calling loop1
loop1 entry 
val it : unit = ()
> 
所以,让!跳过循环1中的msg = inbox.Receive()?我本以为loop2是由返回完成的! loop1和那个让! inbox的赋值..Receive()特定于它使用的异步块。

1 个答案:

答案 0 :(得分:5)

这与所谓的&#34; value restriction&#34;。

有关

因为您为mb1提供了一个显式泛型参数,所以它被编译为函数,而不是值。在没有详细介绍的情况下,编译器必须这样做,以便利用不同的泛型参数访问值。

因此,每次您参考&#34; mb1,实际发生的是一个函数调用,它创建了一个全新的代理,因此两个.Post调用发生在不同的对象上。

要纠正这种情况,请从值中删除泛型参数,从而使其成为真正的一次性计算值:

let mb1 = MailboxProcessor<string>( ...

或确保您只调用一次:

let mb = mb1
mb.Post("data message 1")
mb.Post("data message 2")