[英]F# Making the result of an async workflow available to be displayed by the UI thread
我正在尝试启动需要等待登录到外部系统的 window,因此我想异步处理此登录。 我已经在 F# 交互式 window 中实现了这一点,并且一切都按预期运行,但是当我在程序中运行代码时出现错误:
System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
F# 交互:
#r "WindowsBase"
#r "PresentationCore"
#r "PresentationFramework"
open System
open System.Threading
open System.Windows
let loginTask = async {
Console.WriteLine "Logging in"
Thread.Sleep(5000)
let user = "MyUser"
Console.WriteLine ("Logged in as " + user)
return user
}
let createWindow () =
Console.WriteLine "Creating window"
let window = Window()
window.Title <- "MyWindow"
window.Show()
window
let runWindowWithUser (window:Window) user =
window.Title <- (user + "'s Window")
Console.WriteLine ("Running " + window.Title + " as " + user)
let mainAsync = async {
let window = createWindow()
let! userToken = loginTask |> Async.StartChild
let! user = userToken
runWindowWithUser window user
}
do mainAsync |> Async.StartImmediate
程序.fs
open System.Threading
open System.Windows
[<EntryPoint; STAThread>]
let main argv =
let loginTask = async {
Console.WriteLine "Logging in"
Thread.Sleep(5000)
let user = "MyUser"
Console.WriteLine ("Logged in as " + user)
return user
}
let createWindow () =
Console.WriteLine "Creating window"
let window = Window()
window.Title <- "MyWindow"
window.Show()
window
let runWindowWithUser (window:Window) user =
window.Title <- (user + "'s Window")
Console.WriteLine ("Running " + window.Title + " as " + user)
let mainAsync = async {
let window = createWindow()
let! userToken = loginTask |> Async.StartChild
let! user = userToken
runWindowWithUser window user
}
do mainAsync |> Async.StartImmediate
Console.ReadKey()
1
我明白了, let!
可能导致工作流的 rest 在后台线程上继续,所以我尝试交换线程:
let mainAsync = async {
let context = SynchronizationContext.Current
let window = createWindow()
do! Async.SwitchToThreadPool()
let! user = loginTask
do! Async.SwitchToContext context
runWindowWithUser window user
}
但这似乎并没有像我预期的那样变回原来的线程。
我还尝试将所有 UI 代码排除在异步工作流程之外以避免处理线程,但是我不确定如何从后台线程上完成的工作中获取我的user
信息
let loginTask = async {
Console.WriteLine "Logging in"
Thread.Sleep(5000)
let user = "MyUser"
Console.WriteLine ("Logged in as " + user)
return user
}
Console.WriteLine "Creating window"
let window = Window()
window.Title <- "MyWindow"
window.Show()
let user = loginTask|> Async.StartImmediate // How do I get user information from loginTask without using let! that must be called from an async workflow?
window.Title <- (user + "'s Window")
Console.WriteLine ("Running " + window.Title + " as " + user)
我对 F# 和一般的函数式编程非常陌生。 如何从后台线程上的登录代码中获取user
信息到 UI 线程,以及为什么 F# 交互式 window 中的线程行为不同?
这是我的代码,我删除了 Console.Writeline() 调用,您只需用记录器调用(或 Debug.WriteLine() 或其他)替换它们。
open System
open System.Threading
open System.Windows
let createWindow() =
let window = Window()
window.Title <- "MyWindow"
window.Show()
window
let runWindowWithUser (window: Window) user =
window.Title <- (user + "'s Window")
let loginTask =
async {
Thread.Sleep(1000)
let user = "MyUser"
return user
}
[<EntryPoint; STAThread>]
let main _ =
let app = Application()
app.MainWindow <- createWindow()
app.Startup.Add (fun _ ->
async {
let context = SynchronizationContext.Current
do! Async.SwitchToThreadPool()
let! user = loginTask
do! Async.SwitchToContext context
runWindowWithUser app.MainWindow user
} |> Async.StartImmediate
)
app.Run()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.