Winforms 构建可取消的非阻塞后台工作程序
假设我有一个长时间运行的计算,我想在后台运行,以免阻塞UI线程。所以我将把它包装在一个异步计算中Winforms 构建可取消的非阻塞后台工作程序,winforms,f#,f#-async,Winforms,F#,F# Async,假设我有一个长时间运行的计算,我想在后台运行,以免阻塞UI线程。所以我将把它包装在一个异步计算中 /// Some long-running calculation... let calculation arg = async{ do! Async.Sleep 1000 return arg } 接下来,我需要在一个循环中运行计算,在这个循环中,我切换到另一个线程来执行它,然后返回到UI线程来处理它的结果 /// Execute calculation repeatedly i
/// Some long-running calculation...
let calculation arg = async{
do! Async.Sleep 1000
return arg }
接下来,我需要在一个循环中运行计算,在这个循环中,我切换到另一个线程来执行它,然后返回到UI线程来处理它的结果
/// Execute calculation repeatedly in for-loop and
/// display results on UI thread after every step
open System.Threading
let backgroundLoop uiAction = async {
let ctx = SynchronizationContext.Current
for arg in 0..100 do
do! Async.SwitchToThreadPool()
let! result = calculation arg
do! Async.SwitchToContext ctx
uiAction result }
然后,必须将该循环包装在另一个异步计算中,以提供从UI取消它的方法
/// Event-controlled cancellation wrapper
let cancelEvent = new Event<_>()
let cancellableWorker work = async {
use cToken = new CancellationTokenSource()
Async.StartImmediate(work, cToken.Token)
do! Async.AwaitEvent cancelEvent.Publish
cToken.Cancel() }
这似乎是一个有点努力的东西,我想象作为一个相对常见的工作流程。我们能简化它吗?我遗漏了什么重要的东西吗
然后,必须将该循环包装在另一个异步计算中,以提供从UI取消它的方法
/// Event-controlled cancellation wrapper
let cancelEvent = new Event<_>()
let cancellableWorker work = async {
use cToken = new CancellationTokenSource()
Async.StartImmediate(work, cToken.Token)
do! Async.AwaitEvent cancelEvent.Publish
cToken.Cancel() }
我们能简化它吗
我认为cancelEvent和CancelableWorker在这种情况下是不必要的间接寻址。您可以使用CancellationTokenSource并直接从UI事件取消它,而不是反过来取消令牌的事件
let calculation arg = async {
do! Async.Sleep 1000
return arg }
open System.Threading
let backgroundLoop uiAction = async {
let ctx = SynchronizationContext.Current
for arg in 0..100 do
do! Async.SwitchToThreadPool()
let! result = calculation arg
do! Async.SwitchToContext ctx
uiAction result }
open System.Windows.Forms
let fm = new Form()
let lb = new ListBox(Dock = DockStyle.Fill)
fm.Controls.Add lb
let cToken = new CancellationTokenSource()
fm.Load.Add <| fun _ ->
Async.StartImmediate (backgroundLoop (lb.Items.Add >> ignore), cToken.Token)
lb.KeyDown.Add <| fun _ -> cToken.Cancel()
另外,如果您还没有查看。感谢您展示直接从UI取消是一种可行的选择。但我担心它会涉及一些混乱的可变状态,以防我想重新启动后台循环,因为CancellationTokenSource无法重用。这就是为什么我从Tomas Petricek的另一个有价值的贡献中获得了基于事件的取消。