F#、MailboxProcessor 和 Async 运行 慢?
F#, MailboxProcessor and Async running slow?
背景。
我正在尝试找出 MailboxProcessor。这个想法是将它用作某种状态机并在状态之间传递参数然后退出。有些部分将进行异步通信,所以我在那里做了一个睡眠。
这是一个控制台应用程序,使 Post 不执行任何操作,因为主线程退出并杀死了它后面的所有内容。我正在 main 中制作一个 PostAndReply。
另外,我试过没有
let sleepWorkflow = async
,没有任何区别。
问题。
(我可能做错了什么)
Go24 不是异步的。将 运行Synchronously 更改为 StartImmediate 没有明显的区别。结尾应该在 GetMe 下面的某个地方。同时在Fetch之后打印Done。难道控件不应该在睡眠时返回到主线程吗?
Go24,稍等
go24 1,结束
取 1
完毕
获取我
...
运行 时间慢得要命。在不延迟 Fetch 的情况下,它大约需要 10 秒(秒表)。我认为 F# 线程是轻量级的,应该使用线程池。
根据调试器的说法,它需要 appr 1s 来创建每一个,它看起来像真正的线程。
此外,根据 ProcessExplorer 的说法,更改为 [1..100] 将 "pause" 程序运行 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
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 )
我不确定这是主要问题,但代理代码中的嵌套异步和 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()
背景。
我正在尝试找出 MailboxProcessor。这个想法是将它用作某种状态机并在状态之间传递参数然后退出。有些部分将进行异步通信,所以我在那里做了一个睡眠。 这是一个控制台应用程序,使 Post 不执行任何操作,因为主线程退出并杀死了它后面的所有内容。我正在 main 中制作一个 PostAndReply。 另外,我试过没有
let sleepWorkflow = async
,没有任何区别。
问题。
(我可能做错了什么)
Go24 不是异步的。将 运行Synchronously 更改为 StartImmediate 没有明显的区别。结尾应该在 GetMe 下面的某个地方。同时在Fetch之后打印Done。难道控件不应该在睡眠时返回到主线程吗?
Go24,稍等 go24 1,结束 取 1 完毕 获取我 ...
运行 时间慢得要命。在不延迟 Fetch 的情况下,它大约需要 10 秒(秒表)。我认为 F# 线程是轻量级的,应该使用线程池。 根据调试器的说法,它需要 appr 1s 来创建每一个,它看起来像真正的线程。
此外,根据 ProcessExplorer 的说法,更改为 [1..100] 将 "pause" 程序运行 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
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 )
我不确定这是主要问题,但代理代码中的嵌套异步和 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()