我试图找到一个关于如何使用TryScan
的例子,但是没有找到任何帮助吗?如何在F#中正确使用TryScan
我想要做什么(很简单的例子):我有一个MailboxProcessor
接受 两种类型的消息。
第一个
GetState
返回当前状态。GetState
消息发送相当频繁其他
UpdateState
是非常昂贵(耗时) - 例如,从互联网上下载一些东西,然后相应地更新状态。UpdateState
只被调用很少。
我的问题是 - 消息GetState
被封锁,等到前面的UpdateState
供应。这就是为什么我试图使用TryScan
来处理所有GetState
消息,但没有运气。
我的示例代码:
type Msg = GetState of AsyncReplyChannel<int> | UpdateState
let mbox = MailboxProcessor.Start(fun mbox ->
let rec loop state = async {
// this TryScan doesn't work as expected
// it should process GetState messages and then continue
mbox.TryScan(fun m ->
match m with
| GetState(chnl) ->
printfn "G processing TryScan"
chnl.Reply(state)
Some(async { return! loop state})
| _ -> None
) |> ignore
let! msg = mbox.Receive()
match msg with
| UpdateState ->
printfn "U processing"
// something very time consuming here...
async { do! Async.Sleep(1000) } |> Async.RunSynchronously
return! loop (state+1)
| GetState(chnl) ->
printfn "G processing"
chnl.Reply(state)
return! loop state
}
loop 0
)
[async { for i in 1..10 do
printfn " U"
mbox.Post(UpdateState)
async { do! Async.Sleep(200) } |> Async.RunSynchronously
};
async { // wait some time so that several `UpdateState` messages are fired
async { do! Async.Sleep(500) } |> Async.RunSynchronously
for i in 1..20 do
printfn "G"
printfn "%d" (mbox.PostAndReply(GetState))
}] |> Async.Parallel |> Async.RunSynchronously
如果您尝试运行代码,你会看到,GetState
消息几乎没有处理,因为它等待结果。另一方面,UpdateState
只是失火,从而阻止有效获取状态。
编辑
为我的作品目前的解决办法是这样的一个:
type Msg = GetState of AsyncReplyChannel<int> | UpdateState
let mbox = MailboxProcessor.Start(fun mbox ->
let rec loop state = async {
// this TryScan doesn't work as expected
// it should process GetState messages and then continue
let! res = mbox.TryScan((function
| GetState(chnl) -> Some(async {
chnl.Reply(state)
return state
})
| _ -> None
), 5)
match res with
| None ->
let! msg = mbox.Receive()
match msg with
| UpdateState ->
async { do! Async.Sleep(1000) } |> Async.RunSynchronously
return! loop (state+1)
| _ -> return! loop state
| Some n -> return! loop n
}
loop 0
)
反应来评价:与其他MailboxProcessor
或ThreadPool
执行并行UpdateState
的想法是伟大的,但我目前不需要它。 我只想处理所有GetState
消息,然后处理其他消息。我不在乎在处理UpdateState
时代理被阻止。
我会告诉你什么是对输出的问题:我不认为TryScan
方法将帮助你在这种情况下
// GetState messages are delayed 500 ms - see do! Async.Sleep(500)
// each UpdateState is sent after 200ms
// each GetState is sent immediatelly! (not real example, but illustrates the problem)
U 200ms <-- issue UpdateState
U processing <-- process UpdateState, it takes 1sec, so other
U 200ms 5 requests are sent; sent means, that it is
U 200ms fire-and-forget message - it doesn't wait for any result
and therefore it can send every 200ms one UpdateState message
G <-- first GetState sent, but waiting for reply - so all
previous UpdateState messages have to be processed! = 3 seconds
and AFTER all the UpdateState messages are processed, result
is returned and new GetState can be sent.
U 200ms
U 200ms because each UpdateState takes 1 second
U 200ms
U processing
U
U
U
U
U processing
G processing <-- now first GetState is processed! so late? uh..
U processing <-- takes 1sec
3
G
U processing <-- takes 1sec
U processing <-- takes 1sec
U processing <-- takes 1sec
U processing <-- takes 1sec
U processing <-- takes 1sec
U processing <-- takes 1sec
G processing <-- after MANY seconds, second GetState is processed!
10
G
G processing
// from this line, only GetState are issued and processed, because
// there is no UpdateState message in the queue, neither it is sent
在TryScan调用之后,你有'|>忽略'的事实应该提醒你,你错误地使用了API。 (现在没有时间给出完整的答案,希望有人会打败我。) – Brian 2011-02-02 21:48:03
我知道我以错误的方式使用它。但我还没有发现任何关于使用它的帖子。 – stej 2011-02-02 21:56:06
我认为`TryScan`和`Scan`的意思是等待一条消息,如果没有收到消息则超时。两者之间的唯一区别在于,超时TryScan在Scan引发异常时返回一个选项。 – gradbot 2011-02-03 02:58:27