我目前正在使用VB.NET表单,该表单会根据Excel文件和表单要求的一些其他数据(项目名称,客户名称,使用SQL等)自动创建Word文档。
此过程可以正常运行,大约需要1或2分钟才能完成。
问题是我所有的脚本都在ButtonGenerate.Click
处理程序中。因此,当按下“生成”按钮时,表单窗口会变砖,并且无法取消...
它不应位于Click处理程序中。为此任务打开一个新线程似乎更好。但是多线程对我不是很熟悉。 我尝试使用
启动脚本ThreadPool.QueueUserWorkItem(...
但是我的Generate Sub设置标签并更新主窗体中的进度栏,因此除非使用
,否则我将无法工作Me.Invoke(New MethodInvoker(Sub()
label.Text = "..."
ProgressBar.Value = 10
' ...
End Sub)
每次,我都需要更新表单上的某些内容,而我什至无法检索到任何与此相关的按钮(取消按钮会很不错)。
这基本上是我的代码:
Public Class TestFichesAutomation
Private Sub BtnGenerate_Click(sender As Object, e As EventArgs) Handles BtnGenerate.Click
System.Threading.ThreadPool.QueueUserWorkItem(Sub() Generate())
End Sub
Public Sub Generate()
' Check user options, retrieve Excel Data, SQL, Fill in custom classes, create Word docs (~ 1 minute)
End Sub
那么您将如何处理该脚本?线程甚至是一个好的解决方案吗?
答案 0 :(得分:0)
您应该考虑使用Async/Await structure
如果您需要做的工作是CPU限制的,我喜欢使用Task.Run()
doc here
通过使事件处理程序异步并使其Await
正常工作,可以防止UI锁定并在大多数情况下避免使用Invoke
。
例如:
Private Async Sub Btn_Click(sender As Object, e As EventArgs) Handles Btn.Click
Dim Result As Object = Await Task.Run(Function() SomeFunction())
'after the task returned by Task.Run is completed, the sub will continue, thus allowing you to update the UI etc..
End Sub
对于使用Async/Await
进行进度报告,您可能对this感兴趣
答案 1 :(得分:0)
非常感谢您的帮助^^和有用的文档。
我的应用程序现在打开一个新线程,并使用2个自定义类充当缓冲区:
Private Async Sub Btn_Click(sender As Object, e As EventArgs) Handles Btn.Click
myProgress = New Progress
' a custom class just for the UI with the current task, current SQL connection status and progress value in %
_Options.ProjectName = TextBoxProjectName.Text
_Options.CustomerName = TextBoxCustomerName.Text
...
' Fill in a custom "_Options" private class to act as a buffer between the 2 thread (the user choices)
Loading = New Loading()
Me.Visible = False
Loading.Show() ' Show the Loading window (a ProgressBar and a label : inputLine)
Task.Run(Function() Generate(Progress, _Options))
Me.Visible = True
End Sub
Public Async Function Generate(ByVal myProgress As Progress, ByVal Options As Options) As Task(Of Boolean)
' DO THE LONG JOB and sometimes update the UI :
myProgress.LoadingValue = 50 ' %
myProgress.CurrentTask= "SQL query : " & ...
Me.Invoke(New MethodInvoker(Sub() UpdateLoading()))
' Check if the task has been cancelled ("Cancelled" is changed by a passvalue from the Loading window):
If myProgress.Cancelled = True Then ...
' Continue ...
End Function
Public Shared Sub UpdateLoading()
MyForm.Loading.ProgressBar.Value = myProgress.LoadingValue
MyForm.Loading.inputLine.Text = myProgress.CurrentTask
' ...
End Sub