F#,MailboxProcessor和Async运行缓慢?

F#,MailboxProcessor和Async运行缓慢?,f#,mailboxprocessor,F#,Mailboxprocessor,背景。 我在想MailboxProcessor。其思想是将其用作某种状态机,在状态之间传递参数,然后退出。有些部分将进行异步通信,所以我在那里睡了一觉。 这是一个控制台应用程序,发布帖子没有任何作用,因为主线程退出并杀死它背后的所有东西。我主要在做一个邮差。 而且,我也试过不穿 let sleepWorkflow = async ,没有任何区别 问题。 (我可能做错了什么) Go24不是异步的。将RunSynchronously更改为StartImmediate没有明显的区别。结尾应该在Ge

背景。

我在想MailboxProcessor。其思想是将其用作某种状态机,在状态之间传递参数,然后退出。有些部分将进行异步通信,所以我在那里睡了一觉。 这是一个控制台应用程序,发布帖子没有任何作用,因为主线程退出并杀死它背后的所有东西。我主要在做一个邮差。 而且,我也试过不穿

let sleepWorkflow  = async
,没有任何区别

问题。

(我可能做错了什么)

  • Go24不是异步的。将RunSynchronously更改为StartImmediate没有明显的区别。结尾应该在GetMe下面的某个地方。同时,提取后打印完成。控件不应该在睡眠时返回到主线程吗

    24号,等等 完二四一, 取回1 多恩 抓住我

  • 运行时间非常慢。毫不迟疑,现在大约是10秒(秒表)。我认为F#线程是轻量级的,应该使用threadpool。 根据调试器,创建每个线程需要大约1s的时间,而且看起来像真正的线程

  • 此外,根据ProcessExplorer,更改为[1..100]将“暂停”程序100秒,在此期间创建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
    
    []
    让主argv=
    设a=Mailbox.MessageBasedCounter.DoGo24 1
    设a=Mailbox.MessageBasedCounter.DoFetch 1
    设b=Mailbox.MessageBasedCounter.GetMe
    让任务i=异步{
    //Mailbox.MessageBasedCounter.DoGo24 1
    设a=Mailbox.MessageBasedCounter.DoFetch i
    归还
    }
    让stopWatch=System.Diagnostics.stopWatch.StartNew()
    设x=
    [1..10]
    |>Seq.map任务
    |>异步并行
    |>异步运行
    秒表
    printfn“%f”stopWatch.appeased.total毫秒
    打印fn“a:%a”a
    打印fn“b:%A”b
    printfn“x:%A”x
    0//返回整数退出代码
    
    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 )
    
    模块邮箱
    #诺瓦恩“40”
    类型parserMsg=
    |int的Go24
    |完成
    |获取int*AsyncReplyChannel
    |AsyncReplyChannel的GetMe
    键入MessageBasedCounter()=
    ///创建代理
    静态let agent=MailboxProcessor.Start(有趣的收件箱->
    //消息处理功能
    让rec messageLoop()=异步{
    let!msg=inbox.Receive()
    配味精
    |Go24 n->
    让sleepWorkflow=async{
    打印fn“Go24,等待”
    do!Async.Sleep 4000
    MessageBasedCounter.DoDone()//POST Done。
    打印fn“go24%d,结束”n
    return!messageLoop()
    } 
    Async.RunSynchronously休眠工作流
    |获取(i,repl)->
    让同步=异步{
    printfn“获取%d”i
    do!Async.Sleep 1000
    repl.Reply(“Reply Fetch”+i.ToString())//回复调用者
    return!messageLoop()
    }
    Async.RunSynchronously同步
    |GetMe(repl)->
    让同步=异步{
    printfn“GetMe”
    repl.Reply(“GetMe”)//回复调用方
    return!messageLoop()
    }
    Async.RunSynchronously同步
    |完成->
    让同步=异步{
    打印fn“完成”
    return!messageLoop()
    }
    Async.RunSynchronously同步
    }
    //开始循环
    messageLoop()
    )
    //用于隐藏实现的公共接口
    静态成员DoDone()=agent.Post(完成)
    静态成员DoGo24(i:int)=agent.Post(Go24(i))
    静态成员DoFetch(i:int)=agent.PostAndReply(有趣的回复->获取(i,回复))
    静态成员GetMe=agent.PostAndReply(GetMe)
    
    我不一定确定这是主要问题,但代理代码中的嵌套异步和
    Async.runsynchronously
    看起来可疑

    不需要创建嵌套的异步-只需直接调用
    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
    (但请确保在主体中递归调用主循环):


    啊哈,谢谢你。我正试图恢复你所说的那个特工。看来我的错误是回电话了!异步内的messageLoop()。这并不能恢复代理。我使用外部异步作为解决方法,因为需要将Seq.map作为parallel。运行let x=[1..10]|>Seq.map Mailbox.MessageBasedCounter.DoFetch会在到达10之前中断程序。List.map运行到10时没有问题。只是一个有趣的时刻。
      | 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()