F#,MailboxProcessor和Async运行缓慢?

时间:2015-04-01 15:47:45

标签: f# mailboxprocessor

背景

我想找出MailboxProcessor。我的想法是将它用作某种状态机并在状态之间传递参数然后退出。有些部分会进行异步通信,所以我在那里睡了一觉。 它是一个控制台应用程序,使得Post无所作为,因为主线程退出并杀死它背后的一切。我正在制作一个PostAndReply。 另外,我试过没有

let sleepWorkflow  = async

,没有任何区别。

问题。

(我可能做错了什么)

  1. Go24不是异步。将RunSynchronously更改为StartImmediate没有明显的区别。最终应该在GetMe之下的某个地方。同时在Fetch之后打印Done。是不是控件应该在睡眠时返回主线程?

    Go24,等等 go24 1,结束 取1 完成 GetMe ...

  2. 运行时间很慢。毫不拖延地获取大约10秒(秒表)。我认为F#线程是轻量级的,应该使用threadpool。 根据调试器,它需要appr 1s来创建每个,它看起来像真正的线程。

  3. 此外,更改为[1..100]将"暂停" 100s的程序,根据ProcessExplorer在这段时间内创建100个线程,然后只打印所有内容。我实际上更喜欢更少的线程和缓慢的增加。

    代码。

    Program.fs

    [<EntryPoint>]
    let main argv =
    
    
        let a = Mailbox.MessageBasedCounter.DoGo24 1
        let a = Mailbox.MessageBasedCounter.DoFetch 1
        let b = Mailbox.MessageBasedCounter.GetMe
    
        let task i  = async {
            //Mailbox.MessageBasedCounter.DoGo24 1
            let a = Mailbox.MessageBasedCounter.DoFetch i
            return a
            }
    
        let stopWatch = System.Diagnostics.Stopwatch.StartNew()
    
        let x = 
            [1..10]
                |> Seq.map task
                |> Async.Parallel
                |> Async.RunSynchronously
    
        stopWatch.Stop()
        printfn "%f" stopWatch.Elapsed.TotalMilliseconds
    
        printfn "a: %A" a
        printfn "b: %A" b
    
        printfn "x: %A" x
        0 // return an integer exit code
    

    Mailbox.fs

    module Mailbox
    
    #nowarn "40"
    
    type parserMsg =
        | Go24 of int
        | Done
        | Fetch of int * AsyncReplyChannel<string>
        | GetMe of AsyncReplyChannel<string>
    
    
    type MessageBasedCounter () = 
    
        /// Create the agent
        static let agent = MailboxProcessor.Start(fun inbox -> 
    
            // the message processing function
            let rec messageLoop() = async{
                let! msg = inbox.Receive()
    
                match msg with 
                | Go24 n ->
                    let sleepWorkflow  = async{
                        printfn "Go24, wait"
                        do! Async.Sleep 4000 
                        MessageBasedCounter.DoDone() // POST Done.
                        printfn "go24 %d, end" n
                        return! messageLoop()
                    } 
                    Async.RunSynchronously sleepWorkflow
                | Fetch (i, repl) ->
                    let sync = async{
                        printfn "Fetch %d" i
                        do! Async.Sleep 1000
                        repl.Reply( "Reply Fetch " + i.ToString() ) // Reply to the caller 
                        return! messageLoop()
                    }
                    Async.RunSynchronously sync
    
                | GetMe (repl) ->
                    let sync = async{
                        printfn "GetMe"
                        repl.Reply( "GetMe" ) // Reply to the caller 
                        return! messageLoop()
                    }
                    Async.RunSynchronously sync
                | Done -> 
                    let sync = async{
                        printfn "Done"
                        return! messageLoop()
                    }
                    Async.RunSynchronously sync 
                }
    
            // start the loop 
            messageLoop()
            )
    
        // public interface to hide the implementation
        static member DoDone () = agent.Post( Done )
        static member DoGo24 (i:int) = agent.Post( Go24(i) )
        static member DoFetch (i:int) = agent.PostAndReply( fun reply -> Fetch(i, reply) )
        static member GetMe = agent.PostAndReply( GetMe )
    

1 个答案:

答案 0 :(得分:2)

我不一定确定这是主要问题,但代理代码中的嵌套异步和Async.RunSynchrously看起来很可疑。

您不需要创建嵌套异步 - 您可以直接在match子句的主体中调用异步操作:

// the message processing function
let rec messageLoop() = async{
  let! msg = inbox.Receive()

  match msg with 
  | Go24 n ->
      printfn "Go24, wait"
      do! Async.Sleep 4000 
      MessageBasedCounter.DoDone()
      printfn "go24 %d, end" n
      return! messageLoop()

  | Fetch (i, repl) ->
      (...)

除此之外,重要的是要理解代理只有一个正在运行的体计算实例。因此,如果您阻止代理的主体,则所有其他操作都将排队。

如果你想在后台启动一些任务(比如同步操作)并立即恢复代理,可以在正文中使用Async.Start(但一定要在主要部分中递归调用主循环)身体):

  | Go24 n ->
      // Create work item that will run in the background
      let work = async {
        printfn "Go24, wait"
        do! Async.Sleep 4000 
        MessageBasedCounter.DoDone()
        printfn "go24 %d, end" n }
      // Queue the work in a thread pool to be processed
      Async.Start(work)
      // Continue the message loop, waiting for other messages
      return! messageLoop()