Async.fromBeginEnd 使用 WinForms Begin/EndInvoke

Async.fromBeginEnd with WinForms Begin/EndInvoke

我正在尝试在名为 listControls 的函数之上编写异步包装器,该函数从根控件开始提取表单控件列表。计划是按照Control.BeginInvoke、Control.EndInvoke和Async.FromBeginEnd

来实现

代码如下:

type private ListControls = delegate of unit -> Control list

let asyncListControls (root:Forms.Control) : Async<Control list> = 
    let beginInvoke (_, _) = 
        new ListControls(fun () -> listControls root)
        |> root.BeginInvoke

    let endInvoke result = 
        root.EndInvoke result :?> Control list

    Async.FromBeginEnd (beginInvoke, endInvoke)

我得到的行为是 endInvoke 永远不会执行。有人知道我做错了什么吗?有没有更好的方法?

编辑

为了清楚起见,这个计算最终是用Async.RunSynchronously执行的。

此外,如果我将此代码替换为

/// code below is reproduced from memory and might not compile
let asyncListControls (root:Forms.Control) : Async<Control list> =
     let ctx = WindowsFormsSynchronizationContext.Current
     async {
         do! Async.SwitchToContext ctx
         return listControls root
         do! Async.SwitchToThreadPool ()
     }

...然后它似乎工作,但我不喜欢它,因为强制上下文切换以及异步计算应该建立在 UI 线程

AsyncTask 的不同之处在于它不会在创建时开始。正如 Daniel 指出的那样,您必须使用 Async.RunSynchronously 或其他方法明确启动 Async

我对问题的理解如下:

异步操作是在 UI 线程上用 Async.RunSynchronously 执行的,它正在阻塞线程。

在执行过程中,与 Control.BeginInvoke 一样,它向 UI 线程发送消息。无法处理此消息,因为 UI 线程在 Async.RunSynchronously 上被阻塞,因此出现死锁。