Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在VB.NET窗体中处理长时间运行的任务?_Vb.net_Multithreading_Winforms - Fatal编程技术网

如何在VB.NET窗体中处理长时间运行的任务?

如何在VB.NET窗体中处理长时间运行的任务?,vb.net,multithreading,winforms,Vb.net,Multithreading,Winforms,我目前正在开发一个VB.NET表单,该表单根据Excel文件和表单要求的一些额外数据(项目名称、客户名称、使用SQL等)自动创建Word文档 此程序工作正常,大约需要1或2分钟完成。 问题是我的所有脚本都在按钮生成中。请单击处理程序。因此,当按下“生成”按钮时,窗体窗口被阻塞,无法取消 它不应该在单击处理程序中。为这项漫长的任务打开一条新的线索似乎更好。但是我对多线程不是很熟悉。 我试着用 ThreadPool.QueueUserWorkItem(... 但是我的任务是在主窗体中生成子集标签并

我目前正在开发一个VB.NET表单,该表单根据Excel文件和表单要求的一些额外数据(项目名称、客户名称、使用SQL等)自动创建Word文档

此程序工作正常,大约需要1或2分钟完成。 问题是我的所有脚本都在
按钮生成中。请单击
处理程序。因此,当按下“生成”按钮时,窗体窗口被阻塞,无法取消

它不应该在单击处理程序中。为这项漫长的任务打开一条新的线索似乎更好。但是我对多线程不是很熟悉。 我试着用

ThreadPool.QueueUserWorkItem(...
但是我的任务是在主窗体中生成子集标签并更新进度条,因此除非使用

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



那么你将如何处理这个脚本呢?线程是一个很好的解决方案吗?

您应该考虑使用

如果您需要做的工作受CPU限制,我喜欢使用
Task.Run()
doc

通过使事件处理程序异步并让它等待工作,可以防止UI锁定,并在大多数情况下避免使用
Invoke

例:


对于使用
Async/Await
进行进度报告,您可能会感兴趣

非常感谢您的帮助^^^和有用的文档

我的应用程序现在打开一个新线程,并使用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

这可能会有帮助:虽然我通常倾向于
async
wait
,但可能会重复,因为问题是要更新UI,只要工作在另一个线程上完成,你就不能像在
任务中一样挥手挥动
调用
。运行
——我想这是值得这么做的足够的CPU约束。我想有办法可以解决,但你可能是对的,只使用
调用
。然而,由于在这两种情况下(
Async/Await
或更直接地使用线程池),您都需要调用
invoke
,因此我还是选择
Async/Await
,因为在我看来,这会带来更干净的代码。我当然更喜欢
Async/Await
,我不是有意提出其他建议。然后我误解了您,重新阅读您的评论,我确实可以看出这不是您所说的。同意,与
Async/Await
问题分开:我认为如果您使用原子操作来增加进度变量,如
PercentComplete
,假设您提前计算所需的总工作量,则可以防止使用
invoke
。然后UI线程可以定期检查并更新progressbar。
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