Почему мой MailboxProcessor зависает?

Я не могу понять, почему следующий код висит при вызове GetTotal. Кажется, я не могу отлаживать 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

Кажется, он отлично работает при непосредственном использовании идентичного MailboxProcessor, а не в классе Aggregator.


person Mark Pattison    schedule 29.05.2012    source источник


Ответы (2)


Проблема в том, что вы не запустили агент. Вы можете вызвать Start после создания агента:

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

Кроме того, вы можете создать агент с помощью MailboxProcessor<'T>.Start вместо вызова конструктора (я обычно предпочитаю этот вариант, потому что он выглядит более функциональным):

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

Я предполагаю, что вы не могли отладить агент, потому что код внутри агента на самом деле не работал. Я попытался добавить printfn "Msg: %A" message сразу после вызова Receive внутри агента (чтобы распечатать входящие сообщения для отладки) и заметил, что после вызова Aggregate агент фактически не получил никаких сообщений... (Он был заблокирован только после вызова GetTotal, что жду ответа)

В качестве примечания я бы, вероятно, превратил GetTotal в метод, чтобы вы вызывали GetTotal(). Свойства переоцениваются каждый раз, когда вы к ним обращаетесь, поэтому ваш код делает то же самое, но лучшие практики не рекомендуют использовать свойства, которые выполняют сложную работу.

person Tomas Petricek    schedule 29.05.2012

Вы забыли запустить почтовый ящик:

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
person Robert    schedule 29.05.2012
comment
Это случалось со мной несколько раз, поэтому я легко это заметил. - person Robert; 29.05.2012