F# STA线程异步化

5

I call this this function from the GUI thread:

let updateImageLoop (pprocess : PlotProcess) (target : IUpdatableImageView<'T>) =
    async {
      while target.Continue do
        let context = System.Threading.SynchronizationContext.Current
        do! Async.SwitchToThreadPool()
        System.Threading.Thread.Sleep(10000)
        do! Async.SwitchToContext(context)
        let image = target.CreateImage()
        match image with
        | Some userImage -> do! target.UpdateImageView userImage 
        | None -> ()
    } |> Async.StartImmediate

当执行方法target.UpdateImageView时,会出现问题,会产生一个异常:

调用线程必须是STA,因为许多UI组件需要这样。

我知道这一点,但这就是我所做的。
do! Async.SwitchToContext(context)

如果删除SwitchToContext和SwitchToThreadPool函数,可以消除异常,但GUI仍然会冻结。这是有道理的,但为什么我不能在线程之间切换呢?

生成问题的函数是UpdateImageView。我测试了它,有时异步有时同步。

member this.UpdateImageView  etoimage =
  async {
    let imageview = new Eto.Forms.ImageView()
    imageview.Image <- etoimage
    this.Content <- imageview
  }

编辑 ---

Testing with this code:
let updateImageLoop (pprocess : PlotProcess) (target : IUpdatableImageView<'T>) =
    let context = System.Threading.SynchronizationContext.Current
    let printThread text =
        printfn "[%d] %s" System.Threading.Thread.CurrentThread.ManagedThreadId text
    async {
      while target.Continue do
        printThread "begining"
        do! Async.SwitchToThreadPool()
        printThread "after swith to thread pool"
        let image = target.CreateImage()
        match image with
        | Some userImage -> 
            printThread "before switch to context"
            do! Async.SwitchToContext context 
            printThread "after switch to context"
            target.UpdateImageView userImage 
        | None -> ()
    } |> Async.StartImmediate

打印:

[1] begining 
[4] after swith to thread pool 
[4] before switch to context 
[5] after switch to context

你尝试过在你的程序集中添加 [<STAThread>] 属性吗? - kvb
我的主函数以; [<EntryPoint;STAThread>] 开始,让 main args = ... - hernan
如果异常是由 UpdateImageView 抛出的,可能它期望在创建图像的同一STA线程上调用?也就是说,你使用的库可能期望在同一线程上调用 CreateImageUpdateImageView - Wallace Kelly
1
我注意到在主线程中,System.Threading.SynchronizationContext.Current始终为null。我正在寻找如何解决这个问题。 - hernan
1个回答

4
  1. 使用[< STAThread >]。

  2. 使用guiContext处理GUI相关工作。

在GUI创建(框架初始化)时,请记住guiContext。

let guiContext = System.Threading.SynchronizationContext.Current 

并将其传递到异步GUI执行中。
// the async GUI execute 
async {
            let currentContext = System.Threading.SynchronizationContext.Current 
            do! Async.SwitchToContext(guiContext)
            f() // work on the GUI
            do! Async.SwitchToContext(currentContext)   
}

将等待时间放在一个额外的步骤中,以保持组合性。

1
在我个人的情况下,调用 System.Threading.SynchronizationContext.Current 的语句需要在第一个窗口显示之后。 - hernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接