在我的F# (FsXaml/Code )应用程序中,我想使用一个进度条,而不像在C#中那样使用后台工作人员。基于互联网上的一篇文章(链接是这里),我尝试使用异步工作流。
我基于上述文章中的示例(在某种程度上)创建了代码,但它没有像我预期的那样工作。当前线程(UI线程)仍然被阻塞,就好像没有异步代码一样。没有切换到后台线程。只有在长时间运行操作完成后,进度条才会被激活。删除onThreadPool函数没有任何效果。
我的问题是:我的代码有什么问题,以及如何纠正它?
type MainWindowXaml = FsXaml.XAML<"XAMLAndCodeBehind/MainWindow.xaml">
type MainWindow() as this =
inherit MainWindowXaml()
//....some code....
let ButtonClick _ =
//....some code....
let longRunningOperation() = //....some long running operation (reading from Google Sheets)....
let progressBar() = this.ProgressBar.IsIndeterminate <- true
let doBusyAsync progress operation =
progress
async
{
do! operation
}
|> Async.StartImmediate
let onThreadPool operation =
async
{
let context = System.Threading.SynchronizationContext.Current
do! Async.SwitchToThreadPool()
let! result = operation
do! Async.SwitchToContext context
return result
}
let asyncOperation progress operation =
async { operation }
|> onThreadPool
|> doBusyAsync progress
(progressBar(), longRunningOperation()) ||> asyncOperation
do
//....some code....
this.Button.Click.Add ButtonClick发布于 2021-11-24 00:15:43
您的代码有许多错误。
progressBar(), longRunningOperation()中,您实际上调用了长时间运行的操作,因此所有操作都在这里运行。(据我所知,从不完整的示例中,这只是一个函数调用,而不是另一个异步操作)。operation和progress,但它们只是unit值,实际上什么都不做。async { operation }的异步操作onThreadPool根本不做任何事情。doBusyAsync中,您使用Async.StartImmediate以阻塞方式运行操作(因此这会阻塞线程,即使它正在运行一些实际操作)。async { do! operation },因为这相当于operation。总之,您的代码变得太复杂了。你应该把它简化成一些非常基本的东西,作为第一步。我没有正确的设置来尝试这一点,但我认为下面这样的东西应该能做到这一点:
let ButtonClick _ =
let longRunningOperation() =
// some long-running operation
let asyncOperation() = async {
// Start the progress bar here
let context = System.Threading.SynchronizationContext.Current
do! Async.SwitchToThreadPool()
let result = longRunningOperation()
do! Async.SwitchToContext context
// Display the 'result' in your user interface
// Stop the progress bar here
}
Async.Start(asyncOperation)我删除了所有无用的函数和参数传递,并尽可能地简化了它--这只是您长时间运行的操作,一旦它切换到线程池,它就直接从async调用。您将得到结果,并在切换回原始上下文后,应该能够在用户界面中显示该结果。理想情况下,您可以使longRunningOperation本身是异步的(并使用let!调用它),但是上面的内容应该可以工作。
发布于 2021-11-24 03:38:34
总之,我扩展了TomášPetříček的代码,并根据Jim的评论(关于返回UI线程)编写了与长期运行操作相关的代码。这段代码现在就像一种魅力。我感谢TomášPetříček的友好和详细的回答。
let low = string (this.TextBox2.Text)
let high = string (this.TextBox3.Text)
let path = string (this.TextBox4.Text)
(* longRunningOperation() replaced by textBoxString4() and textBoxString3()
based on the comment by Jim Foye
let longRunningOperation() =
async
{
match textBoxString4 low high >= 0 with
| false -> this.TextBox1.Text <- textBoxString3 low high path
| true -> this.TextBox1.Text <- "Chybný rozdíl krajních hodnot"
}
*)
let textBoxString4() =
async
{
let result = textBoxString4 low high
return result
}
let textBoxString3() =
async
{
//the actual long running operation (reading data
//from Google Sheets)
let result = textBoxString3 low high path
return result
}
let asyncOperation() =
async
{
let context = System.Threading.SynchronizationContext.Current
this.ProgressBar2.IsIndeterminate <- true
do! Async.SwitchToThreadPool()
(*let! result = longRunningOperation() throws an exception
"The calling thread cannot access this object because
a different thread owns it."
*)
let! result4 = textBoxString4()
let! result3 = textBoxString3()
do! Async.SwitchToContext context
match result4 >= 0 with
| false -> this.TextBox1.Text <- result3
| true -> this.TextBox1.Text <- "Chybný rozdíl krajních hodnot"
this.ProgressBar2.IsIndeterminate <- false
}
Async.StartImmediate(asyncOperation())//not working with Async.Starthttps://stackoverflow.com/questions/70088957
复制相似问题