簡體   English   中英

結合使用帶有回復通道的MailboxProcessor來創建按順序返回值的有限代理

[英]Use a MailboxProcessor with reply-channel to create limited agents that return values in order

基本上,我想將以下內容更改為有限的線程解決方案,因為在我的情況下,計算列表太大,產生了太多的線程,並且我想用更少的線程進行實驗和衡量性能。

// the trivial approach (and largely my current situation)
let doWork() = 
    [1 .. 10]
    |> List.map (fun i -> async { 
        do! Async.Sleep (100 * i)   // longest thread will run 1 sec
        return i * i                // some complex calculation returning a certain type
        })
    |> Async.Parallel
    |> Async.RunSynchronously       // works, total wall time 1s

我的新方法是從Tomas Petricek此在線代碼段借用/啟發該代碼 (經過測試,它可以工作,但我需要它返回一個值,而不是單位)。

type LimitAgentMessage = 
  | Start of Async<int> *  AsyncReplyChannel<int>
  | Finished

let threadingLimitAgent limit = MailboxProcessor.Start(fun inbox -> async {

    let queue = System.Collections.Generic.Queue<_>()
    let count = ref 0
    while true do
        let! msg = inbox.Receive() 
        match msg with 
        | Start (work, reply) -> queue.Enqueue((work, reply))
        | Finished -> decr count
        if count.Value < limit && queue.Count > 0 then
            incr count
            let work, reply = queue.Dequeue()
            // Start it in a thread pool (on background)
            Async.Start(async { 
                let! x = work 
                do! async {reply.Reply x }
                inbox.Post(Finished) 
            }) 
  })


// given a synchronous list of tasks, run each task asynchronously, 
// return calculated values in original order
let worker lst = 
    // this doesn't work as expected, it waits for each reply
    let agent = threadingLimitAgent 10
    lst 
    |> List.map(fun x ->            
        agent.PostAndReply(
            fun replyChannel -> Start(x, replyChannel)))

現在,有了這個,原始代碼將變為:

let doWork() = 
    [1 .. 10]
    |> List.map (fun i -> async { 
        do! Async.Sleep (100 * i)   // longest thread will run 1 sec
        return i * i                // some complex calculation returning a certain type
        })
    |> worker       // worker is not working (correct output, runs 5.5s)

總而言之,輸出是正確的(它確實計算並傳播了答復),但在(有限組)線程中卻不是這樣。

我一直在玩耍,但是我想我沒有找到明顯的東西(此外,有人知道,有人可能喜歡有限線程郵箱處理器的想法,該處理器按順序返回其計算結果)。

問題是對agent.PostAndReply的調用。 PostAndReply將阻塞,直到工作完成。 List.map調用此List.map將導致工作按順序執行。 一種解決方案是使用PostAndAsyncReply ,它不會阻塞並且還會返回一個異步句柄以獲取結果。

let worker lst = 
    let agent = threadingLimitAgent 10
    lst 
    |> List.map(fun x ->            
        agent.PostAndAsyncReply(
            fun replyChannel -> Start(x, replyChannel)))
    |> Async.Parallel

let doWork() = 
    [1 .. 10]
    |> List.map (fun i -> async { 
        do! Async.Sleep (100 * i)  
        return i * i               
        })
    |> worker      
    |> Async.RunSynchronously

當然,那只是一種可能的解決方案(獲取所有異步句柄並並行等待它們)。

暫無
暫無

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

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