multithreading - F# 使异步工作流的结果可供 UI 线程显示
问题描述
我正在尝试启动一个需要等待登录到外部系统的窗口,所以我想异步处理这个登录。我已经在 F# 交互式窗口中实现了这一点,并且一切都按预期运行,但是当我在程序中运行代码时出现错误:
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!
可能会导致工作流程的其余部分在后台线程上继续,所以我尝试交换线程:
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# 交互式窗口中的线程行为不同?
解决方案
这是我的代码,我删除了 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()
推荐阅读
- php - Symfony - 日期时间作为参数
- c# - 通过 Sql 存储过程在 ASP.Net MVC 模型中保存多个结果集而不使用 DataSet
- amazon-web-services - 如何在aws上创建子域并从godaddy dns域指向它
- java - 如何在线程中使用 SensorManager 正确实现传感器?
- postgresql - 在函数中更改模式名称
- python - 我可以更有效地拆分包含元组/无混合的列吗?
- flutter - 如果启用则切换
- c# - HttpClient POST 与数据
- java - 使用 ISO 4217 的 CurrencyCode 格式化 pl_PL 价格
- mysql - 将 mysql-db 挂载到 docker-container