F# 使异步工作流的结果可由 UI 线程显示
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
Program.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# 交互式 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()
我正在尝试启动一个 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
Program.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# 交互式 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()