ユーザーの操作に基づいてUIを更新するエージェントを作成しようとしています。ユーザーがボタンをクリックすると、GUIがリフレッシュされます。モデルの作成には長い時間がかかりますので、ユーザーが他のボタンをクリックすると、準備がキャンセルされ、新しいものが開始されることが望まれます。私がこれまで持って何Async.TryCancelledはAsync.RunSynchronouslyで動作しません
:
open System.Threading
type private RefreshMsg =
| RefreshMsg of AsyncReplyChannel<CancellationTokenSource>
type RefresherAgent() =
let mutable cancel : CancellationTokenSource = null
let doSomeModelComputation i =
async {
printfn "start %A" i
do! Async.Sleep(1000)
printfn "middle %A" i
do! Async.Sleep(1000)
printfn "end %A" i
}
let mbox =
MailboxProcessor.Start(fun mbx ->
let rec loop() = async {
let! msg = mbx.Receive()
match msg with
| RefreshMsg(chnl) ->
let cancelSrc = new CancellationTokenSource()
chnl.Reply(cancelSrc)
let update = async {
do! doSomeModelComputation 1
do! doSomeModelComputation 2
//do! updateUI // not important now
}
let cupdate = Async.TryCancelled(update, (fun c -> printfn "refresh cancelled"))
Async.RunSynchronously(cupdate, -1, cancelSrc.Token)
printfn "loop()"
return! loop()
}
loop())
do
mbox.Error.Add(fun exn -> printfn "Error in refresher: %A" exn)
member x.Refresh() =
if cancel <> null then
// I don't handle whether the previous computation finished
// I just cancel it; might be improved
cancel.Cancel()
cancel.Dispose()
cancel <- mbox.PostAndReply(fun reply -> RefreshMsg(reply))
printfn "x.Refresh end"
//sample
let agent = RefresherAgent()
agent.Refresh()
System.Threading.Thread.Sleep(1500)
agent.Refresh()
私は(x.Refresh()はスレッドセーフで、それはUIスレッド上で呼び出された)要求ごとにCancellationTokenSource
を返し、変更可能な変数に格納します。 Refresh()が初めて呼び出された場合、キャンセルソースが返されます。 Refresh()が2回目に呼び出された場合、Cancelを呼び出し、Async.RunSynchronouslyを実行する非同期タスクを中止する必要があります。
ただし、例外が発生します。私のサンプルからの出力は、私がこのことについて考えるように、エージェントが実行されるスレッドは、右、interrputedれたので、それは、意味をなすかもしれない今
x.Refresh end
start 1
middle 1
end 1
refresh cancelled
Error in refresher: System.OperationCanceledException: The operation was canceled.
at Microsoft.FSharp.Control.AsyncBuilderImpl.commit[a](Result`1 res)
のですか?しかし、どのようにして目的の動作を達成できますか?
私は、エージェントが新しいメッセージを消費し続けることができるように、エージェントの内部で非同期ワークフローをキャンセルする必要があります。メールボックスプロセッサを使用するのはなぜですか? 1つのスレッドだけがUIモデルを作成しようとしていることが保証されているので、リソースを節約します。
いくつかのWebサービスからデータをダウンロードしてUIモデルを作成するとします。そのため、非同期呼び出しを使用しています。ユーザーがコンボを変更して他のオプションを選択すると、古い値のWebサービス(=非同期呼び出しをキャンセル)のクエリを停止し、新しい値で新しいモデルベースのWebサービス呼び出しを作成したいと考えています。
私の解決策の代わりに使うことができ、私の問題を解決する提案も歓迎です。
背景をよりよく説明する段落を追加しました。 – stej
あなたはexcをキャッチすることは解決策だと思っていますが、よりよいものがあることを願っています。たぶん 'Async'の他のメソッドの組み合わせ(' TryCancelled'と 'RunSynchronously'以外)。 – stej
私は私の答えに提案を追加しました...私はこれをテストすることはできませんが、私は*予想どおりに動作するはずだと思う* – Carsten