簡體   English   中英

F#,MailboxProcessor和Async運行緩慢?

[英]F#, MailboxProcessor and Async running slow?

背景。

我試圖找出MailboxProcessor。 想法是將其用作某種狀態機,並在狀態之間傳遞參數,然后退出。 有些部分將進行異步通信,所以我在那里睡覺了。 這是一個控制台應用程序,發布Post不會執行任何操作,因為主線程會退出並殺死其背后的所有內容。 我主要是在做一個PostAndReply。 另外,我嘗試了

let sleepWorkflow  = async

,沒有任何區別。

問題。

(我可能做錯了)

  1. Go24不是異步的。 將RunSynchronously更改為StartImmediate不會產生明顯的區別。 末尾應位於GetMe下方。 同時在提取后打印完成。 難道不應該在睡眠時將控件返回到主線程嗎?

    Go24,等待go24 1,結束獲取1完成GetMe ...

  2. 運行時間太慢了。 毫不延遲地,獲取時間約為10秒(秒表)。 我認為F#線程是輕量級的,應該使用線程池。 根據調試器,創建每個應用程序都需要大約1秒鍾的時間,並且看起來像真實線程。

同樣,更改為[1..100]將“暫停”程序100秒鍾,根據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 )

我不一定要確定這是主要問題,但是代理代碼中嵌套的asyncs和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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM