2012-05-29 6 views
3

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

ответ

7

Проблема в том, что вы не запустили агента. Вы можете либо позвонить 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, который avaits ответить)

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

+0

Спасибо - вы получили там первые ... –

4

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

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 
+0

Спасибо - чувство глупо сейчас ... –

+0

Это случилось со мной несколько раз, вот почему я был в состоянии это довольно легко. – Robert

Смежные вопросы