1

这个问题是基于Async.TryCancelled 不适用于 Async.RunSynchronously看起来很复杂,所以我将削减一个我尝试解决的简单部分。

假设我有这个功能:

let prepareModel () = 
    async {
        // this might take a lot of time (1-50seconds)
        let! a = ...
        let! b = ...
        let! res = combine a b
        return res
    }
 let updateUI model =
    runOnUIThread model

prepareModel准备应该显示给用户的数据。updateUI刷新 UI(删除旧控件并根据新数据创建新 ctl)。

问题:我应该如何调用这两个函数以便可以prepareModel随时取消?

流量是

  • 用户点击刷新
    • prepareModel(1) 异步启动和运行,因此 UI 是响应式的,用户可以使用应用程序
  • 用户更改数据并再次单击刷新
    • prepareModel(1) from 被取消,new prepareModel(2) 开始
  • 用户更改数据并再次单击刷新
    • prepareModel(2) 被取消并prepareModel开始新的 (3)
    • ..
    • prepareModel(n) 完成
    • updateUI在 UI 线程上运行,重绘 UI

(我的第一个解决方案基于MailboxProcessor确保只prepareModel执行一个,请参阅Async.TryCancelled 不适用于 Async.RunSynchronously但当我对此进行试验时,它不是没有错误的)

4

1 回答 1

5

一种可能的方法是在后台使用异步启动工作流Async.Start(然后它应该是可取消的)。要在最后重绘 UI,您可以使用Async.SwitchToContext确保工作流的最后一部分在 UI 上执行。这是一个草图:

// Capture current synchronization context of the UI
// (this should run on the UI thread, i.e. when starting)
let syncContext = System.Threading.SynchronizationContext.Current

// Cancellation token source that is used for cancelling the
// currently running workflow (this can be mutable)
let cts = ref (new CancellationTokenSource())

// Workflow that does some calculations and then updates gui
let updateModel () =   
    async {  
        // this might take a lot of time (1-50seconds)  
        let! a = ...  
        let! b = ...  
        let! res = combine a b 

        // switch to the GUI thread and update UI
        do! Async.SwitchToContext(syncContext)
        updateUserInterface res
    }  

// This would be called in the click handler - cancel the previous
// computation, creat new cancellation token & start the new one
cts.Cancel()
cts := new CancellationTokenSource()
Async.Start(updateModel(), cts.Token)
于 2012-03-08T10:29:20.577 回答