首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >F#,MailboxProcessor和异步运行缓慢?

F#,MailboxProcessor和异步运行缓慢?
EN

Stack Overflow用户
提问于 2015-04-01 15:47:44
回答 1查看 738关注 0票数 1

背景.

我正在努力找出MailboxProcessor。其思想是将其用作某种状态机,并在各州之间传递参数,然后退出。有些部分将有异步通信,所以我在那里睡了一觉。它是一个控制台应用程序,使一个Post什么也不做,因为主线程退出并杀死了它背后的一切。我主要在做一个PostAndReply。而且,我也试过

代码语言:javascript
复制
let sleepWorkflow  = async

没有任何区别。

问题.

(我可能做错了什么事)

  1. Go24不是异步的。将RunSynchronously更改为StartImmediate没有明显的区别。结尾应该在GetMe下面的某个地方。同时完成的是打印后提取。控件不是应该返回到睡眠时的主线程吗? Go24,等待go24 1,完成GetMe .
  2. 运行时间太慢了。这是大约10秒(秒表)。我认为F#线程是轻量级的,应该使用线程池。根据调试器,创建每个线程需要appr 1,它看起来像真正的线程。

此外,更改为1..100将“暂停”程序为100,根据ProcessExplorer 100线程是在这段时间内创建的,只有在那时所有的东西都被打印出来。实际上,我更喜欢更少的线程和缓慢增长。

代码。

Program.fs

代码语言:javascript
复制
[<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

代码语言:javascript
复制
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 )
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-04-01 16:03:19

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

您不需要创建嵌套的异步--只需在match子句的正文中直接调用异步操作:

代码语言:javascript
复制
// 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 (但请确保在主体部分递归调用主循环):

代码语言:javascript
复制
  | 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()
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29395251

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档