TryScan
の使い方の例を見つけようとしていましたが、何も見つかりませんでした。TryScanをF#で正しく使う方法
私は何をしたいですか(簡略化した例):MailboxProcessor
には の2種類のメッシュがあります。
最初の1つは
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
)
反応:並列にUpdateState
を実行し、他のMailboxProcessor
またはThreadPool
とのアイデアは素晴らしいです、私はそれを現在必要としません。 私がしたかったのは、すべてGetState
のメッセージを処理し、その後は他のメッセージを処理することでした。私は処理中にUpdateState
エージェントがブロックされていることに気にしません。
私は出力に問題が何であったかを紹介します:
// 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'コールの後に' |> ignore'を付けても、あなたがAPIを間違って使っているという事実を警告しなければなりません。 (今、完全な答えのための時間がない、誰かが私にそれを打つことを願って) – Brian
私は間違った方法でそれを使用することを知っています。しかし、私はそれをまったく使用することについての投稿は見つかりませんでした。 – stej
「TryScan」と「Scan」のポイントは、メッセージが受信されない場合はタイムアウトを待つことです。この2つの唯一の違いは、タイムアウト時にTryScanがオプションを返し、Scanが例外を発生させることです。 – gradbot