Async.TryCancelled不与Async.RunSynchronously工作 [英] Async.TryCancelled doesn't work with Async.RunSynchronously

查看:180
本文介绍了Async.TryCancelled不与Async.RunSynchronously工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个基于用户交互的UI更新代理。如果用户点击一个按钮,图形用户界面应该是刷新。模型的preparation需要很长的时间,所以最好是当用户点击其他按钮时,preparation被取消,而新的开始。

I try to create an agent that updates UI based on user interaction. If user clicks on a button, the GUI should be refreshed. The preparation of model takes a long time, so it is desirable that if user clicks on other button, the preparation is cancelled and the new one is started.

我到目前为止有:

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()

我返回 CancellationTokenSource 为每个请求并将其存储在一个可变变量(x.Refresh()是线程安全的,它被称为在UI线程)。
如果刷新()被调用的第一次,返回取消源。如果刷新()被调用第二次,我呼吁取消它应该退出,我通过Async.RunSynchronously运行异步任务。

I return a CancellationTokenSource for each request and store it in a mutable variable (the x.Refresh() is thread safe, it is called on UI thread). If Refresh() is called for the first time, the cancellation source is returned. If Refresh() is called for the second time, I call Cancel which should abort the async task that I run through Async.RunSynchronously.

然而,一个异常。从我的样本的输出是

However, an exception is raised. The output from my sample is

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)

现在,我想这一点,它可能是有意义的,因为在其上运行的代理线程,被interrputed,对不对?但是,我怎么达到预期的行为?

Now as I think about this, it might make sense, because the thread on which the agent runs, was interrputed, right? But, how do I achieve the desired behaviour?

我要取消代理异步里面的工作流程,以便代理可以继续消耗新消息。为什么我用邮箱处理器?因为它保证只有一个线程试图创建UI模式,让我节省资源。

让我们假设我从多个Web服务下载数据创建UI模型,这就是为什么我使用异步调用。当用户改变组合,并选择其他选项,我想停下来查询Web服务(=取消异步调用)与旧的价值,并希望创造新的示范基地OD Web服务的新价值打电话。

这是我可以改用我的解决方案,并能解决我的问题任何建议,也是受欢迎的。

Any suggestion that I can use instead of my solution and will solve my problem, is also welcome.

推荐答案

我在试图理解你想达到什么困难。但是,也许这并不重要 - 错误只是说,你与 RunSynchronously 执行工作流被取消(RunSynchronously会抛出异常) - 所以你可以用这个调用成尝试匹配块,只是忽略了OC-异常

I have difficulties in trying to understand what you want to achieve. But maybe this does not matter - the error just says that the workflow you are executing with RunSynchronously was canceled (RunSynchronously will throw the exception) - so you can wrap this call into a try-match block and just ignore the OC-Exception

一个更好的选择可能是重构cupdate和试戴比赛这里面 - 你甚至可以把在TryCancelled这个问题,如果你直接捕捉到OC-例外;)

a better option might be to refactor your cupdate and to the try-match inside of this - you can even bring the in TryCancelled into it if you catch the OC-Exceptions directly ;)

let update = 
   async {
      try
         do! doSomeModelComputation 1
         do! doSomeModelComputation 2
      with
      | :? OperationCanceledException -> 
           printfn "refresh cancelled"
   }
Async.RunSynchronously(update, -1, cancelSrc.Token)

但我还是你为什么要这个同步方式没有得到部分

But I still don't get the part why you want this Synchronously

这篇关于Async.TryCancelled不与Async.RunSynchronously工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆