如何处理VB.NET表单中长期运行的任务?

时间:2019-07-19 11:18:29

标签: vb.net multithreading winforms

我目前正在使用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


那么您将如何处理该脚本?线程甚至是一个好的解决方案吗?

2 个答案:

答案 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
相关问题