[英]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.