FsXaml 应用程序中的异步进度条
Progress bar with async in FsXaml application
在我的 F# (FsXaml/Code 后面) 应用程序中,我想使用进度条而不使用一个后台工作者,就像我在 C# 中所做的那样。根据互联网上的一篇文章(link 是 here),我尝试使用异步工作流。
我根据(在某种程度上)上述文章中的示例创建了代码,但它没有像我预期的那样工作。当前线程(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
您的代码有很多问题。
首先,在 progressBar(), longRunningOperation()
中,您实际上调用了长 运行ning 操作,所以它都在这里得到 运行。 (据我从你的不完整示例中猜测,这只是一个函数调用,而不是另一个异步操作)。
然后您传递结果 operation
和 progress
,但这些只是 unit
值,实际上没有任何作用。
因此,您传递给 onThreadPool
的异步操作 async { operation }
根本不执行任何操作。
在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)
我删除了所有无用的函数和参数传递并尽可能简化它 - 它只是你的长 运行ning 操作,一旦切换到线程池就直接从 async
调用.您得到了结果,并且在切换回原始上下文后,应该能够在您的用户界面中显示它。理想情况下,您可以使 longRunningOperation
本身异步(并使用 let!
调用它),但上面的方法应该有效。
综上所述,我根据 Jim Foye 的评论(关于跳回到 UI 线程),使用与长 运行 操作相关的代码扩展了 Tomáš Petříček 的代码。该代码现在就像一个魅力。感谢 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.Start
在我的 F# (FsXaml/Code 后面) 应用程序中,我想使用进度条而不使用一个后台工作者,就像我在 C# 中所做的那样。根据互联网上的一篇文章(link 是 here),我尝试使用异步工作流。
我根据(在某种程度上)上述文章中的示例创建了代码,但它没有像我预期的那样工作。当前线程(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
您的代码有很多问题。
首先,在
progressBar(), longRunningOperation()
中,您实际上调用了长 运行ning 操作,所以它都在这里得到 运行。 (据我从你的不完整示例中猜测,这只是一个函数调用,而不是另一个异步操作)。然后您传递结果
operation
和progress
,但这些只是unit
值,实际上没有任何作用。因此,您传递给
onThreadPool
的异步操作async { operation }
根本不执行任何操作。在
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)
我删除了所有无用的函数和参数传递并尽可能简化它 - 它只是你的长 运行ning 操作,一旦切换到线程池就直接从 async
调用.您得到了结果,并且在切换回原始上下文后,应该能够在您的用户界面中显示它。理想情况下,您可以使 longRunningOperation
本身异步(并使用 let!
调用它),但上面的方法应该有效。
综上所述,我根据 Jim Foye 的评论(关于跳回到 UI 线程),使用与长 运行 操作相关的代码扩展了 Tomáš Petříček 的代码。该代码现在就像一个魅力。感谢 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.Start