简体   繁体   English

为什么我的邮箱处理器挂起了?

[英]Why is my MailboxProcessor hanging?

I can't work out why the following code is hanging at the call to GetTotal . 我无法弄清楚为什么下面的代码挂起来调用GetTotal I don't seem to be able to debug inside the MailboxProcessor, so it's hard to see what's going on. 我似乎无法在MailboxProcessor内部进行调试,因此很难看到发生了什么。

module Aggregator

open System

type Message<'T, 'TState> =
    | Aggregate of 'T
    | GetTotal of AsyncReplyChannel<'TState>

type Aggregator<'T, 'TState>(initialState, f) =
    let myAgent = new MailboxProcessor<Message<'T, 'TState>>(fun inbox ->
        let rec loop agg =
            async {
                let! message = inbox.Receive()
                match message with
                    | Aggregate x -> return! loop (f agg x)
                    | GetTotal replyChannel ->
                        replyChannel.Reply(agg)
                        return! loop agg
            }
        loop initialState
        )

    member m.Aggregate x = myAgent.Post(Aggregate(x))
    member m.GetTotal = myAgent.PostAndReply(fun replyChannel -> GetTotal(replyChannel))

let myAggregator = new Aggregator<int, int>(0, (+))

myAggregator.Aggregate(3)
myAggregator.Aggregate(4)
myAggregator.Aggregate(5)

let totalSoFar = myAggregator.GetTotal
printfn "%d" totalSoFar

Console.ReadLine() |> ignore

It seems to work fine when using an identical MailboxProcessor directly, rather than wrapping in the Aggregator class. 它直接使用相同的MailboxProcessor似乎工作正常,而不是包装在Aggregator类中。

The problem is that you did not start the agent. 问题是你没有启动代理。 You can either call Start after you create the agent: 您可以在创建代理后调用Start

let myAgent = (...)
do myAgent.Start()

Alternatively, you can create the agent using MailboxProcessor<'T>.Start instead of calling the constructor (I usually prefer this option, because it looks more functional): 或者,您可以使用MailboxProcessor<'T>.Start创建代理,而不是调用构造函数(我通常更喜欢这个选项,因为它看起来更实用):

let myAgent = MailboxProcessor<Message<'T, 'TState>>.Start(fun inbox ->  (...) )

I suppose that you couldn't debug the agent, because the code inside agent wasn't actually running. 我想您无法调试代理,因为代理内部的代码实际上并未运行。 I tried adding printfn "Msg: %A" message right after the call to Receive inside the agent (to print incoming messages for debugging) and I noticed that, after calling Aggregate , no messages were actually received by the agent... (It only blocked after calling GetTotal , which avaits reply) 我尝试在代理中调用Receive之后立即添加printfn "Msg: %A" message (打印传入的消息以进行调试),我注意到,在调用Aggregate ,代理实际上没有收到任何消息......(它只有在调用GetTotal后才会被阻止,这会回复

As a side-note, I would probably turn GetTotal into a method, so you'd call GetTotal() . 作为旁注,我可能会将GetTotal变成一个方法,所以你要调用GetTotal() Properties are re-evaluated each time you access them, so your code does the same thing, but best practices don't recommend using properties that do complex work. 每次访问属性时都会重新评估属性,因此您的代码执行相同的操作,但最佳做法不建议使用执行复杂工作的属性。

You forgot to start the mailbox: 你忘了启动邮箱:

open System

type Message<'T, 'TState> =
    | Aggregate of 'T
    | GetTotal of AsyncReplyChannel<'TState>

type Aggregator<'T, 'TState>(initialState, f) =
    let myAgent = new MailboxProcessor<Message<'T, 'TState>>(fun inbox ->
        let rec loop agg =
            async {
                let! message = inbox.Receive()
                match message with
                    | Aggregate x -> return! loop (f agg x)
                    | GetTotal replyChannel ->
                        replyChannel.Reply(agg)
                        return! loop agg
            }
        loop initialState
        )

    member m.Aggregate x = myAgent.Post(Aggregate(x))
    member m.GetTotal = myAgent.PostAndReply(fun replyChannel -> GetTotal(replyChannel))
    member m.Start() = myAgent.Start()

let myAggregator = new Aggregator<int, int>(0, (+))

myAggregator.Start()

myAggregator.Aggregate(3)
myAggregator.Aggregate(4)
myAggregator.Aggregate(5)

let totalSoFar = myAggregator.GetTotal
printfn "%d" totalSoFar

Console.ReadLine() |> ignore

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM