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 - Fatal编程技术网

Vb.net 如何在单独的线程中创建加载程序?

Vb.net 如何在单独的线程中创建加载程序?,vb.net,multithreading,Vb.net,Multithreading,我有一个主窗体,它将执行一些长操作。同时,我尝试显示已执行操作的百分比 所以我创建了第二种形式,如下所示: Private Delegate Sub DoubleFunction(ByVal D as Double) Private Delegate Sub EmptyFunction() Public Class LoaderClass Inherits Form 'Some properties here Public Sub DisplayPercentage

我有一个主窗体,它将执行一些长操作。同时,我尝试显示已执行操作的百分比

所以我创建了第二种形式,如下所示:

Private Delegate Sub DoubleFunction(ByVal D as Double)
Private Delegate Sub EmptyFunction()

Public Class LoaderClass
    Inherits Form

    'Some properties here

    Public Sub DisplayPercentage(Value as Double)
        If Me.InvokeRequired then
            dim TempFunction as New DoubleFunction(addressof DisplayPercentage)
            Me.Invoke(TempFunction, Value)
        Else
            Me.PercentageLabel.text = Value
        End if 
    End sub

    Public Sub CloseForm()
        If Me.InvokeRequired Then
            Dim CloseFunction As New EmptyFunction(AddressOf CloseForm)
            Me.Invoke(CloseFunction)
        Else
            Me.Close()
        End If

        FormClosed = True
    End Sub
End class
我的主要分包商(预计将执行长期操作的分包商)采用另一种形式,如下所示:

Private Sub InitApplication
    Dim Loader as new LoaderClass
    Dim LoaderThread as new thread(Sub()
                                    Loader.ShowDialog()
                                    End sub)

    LoaderThread.start()

    Loader.DisplayPercentage(1/10)
    LoadLocalConfiguration()

    Loader.DisplayPercentage(2/10)
    ConnectToDataBase()

    Loader.DisplayPercentage(3/10)
    LoadInterfaceObjects()

    Loader.DisplayPercentage(4/10)
    LoadClients()

    ...

    Loader.CloseForm()
End sub 
这段代码几乎95%的时间都在工作,但有时我会在sub DisplayPercentage的某个地方遇到线程异常。我完全没有做任何更改,只是再次点击开始按钮,调试器继续执行,没有任何问题

我得到的例外情况是:跨线程操作无效:控件“LoaderClass”是从事件中创建的线程以外的线程访问的,尽管我使用的是:if invokererequired

有人知道那个代码有什么问题吗

谢谢。

Me.Invoke(TempFunction,Value)

应该是:

Me.Invoke(TempFunction,new Object(){Value})

因为带有参数的重载需要一个参数数组


值位于当前线程中函数的堆栈上。您需要在GC堆上分配内存,并将该值复制到该内存中,以便即使在本地堆栈被销毁后,该值也可供其他线程使用。

这是一个标准的线程错误,称为“竞争条件”。代码的基本问题是,只有在创建对话框的本机窗口后,InvokeRequired属性才能准确。问题是你不会等那一刻。您启动的线程需要时间来创建对话框。当InvokeRequired仍然返回false时,它会崩溃,但不到一秒钟,窗口就会创建,Invoke()现在会大声反对在工作线程上被调用

这需要联锁,您必须使用AutoResetEvent。在对话框的加载事件处理程序中调用其Set()方法。在InitApplication()中调用其WaitOne()方法

这不是该代码的唯一问题。您的对话框与应用程序中的其他窗口也没有Z顺序关系。它在另一个窗口后显示的可能性非零

以及SystemEvents类引起的一种特别严重的问题。它需要在UI线程上触发事件。它不知道哪个线程是UI线程,它猜测订阅事件的第一个线程就是那个UI线程。如果这是您的对话框,当它使用(比如)进度条时,结果会非常糟糕。它使用SystemEvents知道何时重新绘制自身。当一个SystemEvents现在在错误的线程上引发时,在对话框关闭后很长时间内,您的程序将崩溃并烧坏

你害怕够了吗?不要这样做。仅在UI线程上显示UI,仅在工作线程上执行慢速非UI代码


谢谢你的建议。请问怎么做?我应该去哪里 添加调用

假设您选择将主窗体的“加载”代码保留在主UI线程中(可能从Load()事件调用),将LoaderClass()设置为项目-->属性中的“启动屏幕”

以下是LoaderClass()的外观:

Public Class LoaderClass

    Private Delegate Sub DoubleFunction(ByVal D As Double)

    Public Sub DisplayPercentage(Value As Double)
        If Me.InvokeRequired Then
            Dim TempFunction As New DoubleFunction(AddressOf DisplayPercentage)
            Me.Invoke(TempFunction, Value)
        Else
            Me.PercentageLabel.text = Value
        End If
    End Sub

End Class
*这与您的情况相同,但我已将该委托移动到类中

*请注意,您不需要CloseForm()方法,因为一旦主窗体完全加载,框架将自动关闭初始屏幕

现在,在主窗体中,您可以使用
My.Application.SplashScreen
抓取显示的启动屏幕实例,并将其转换回LoaderClass()。然后只需在适当的时间使用适当的值调用DisplayPercentage()方法:

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        InitApplication()
    End Sub

    Private Sub InitApplication()
        Dim Loader As LoaderClass = DirectCast(My.Application.SplashScreen, LoaderClass)

        Loader.DisplayPercentage(1 / 10)
        LoadLocalConfiguration()

        Loader.DisplayPercentage(2 / 10)
        ConnectToDataBase()

        Loader.DisplayPercentage(3 / 10)
        LoadInterfaceObjects()

        Loader.DisplayPercentage(4 / 10)
        LoadClients()

        ' Loader.CloseForm() <-- This is no longer needed..."Loader" will be closed automatically!
    End Sub

    Private Sub LoadLocalConfiguration()
        System.Threading.Thread.Sleep(1000) ' simulated "work"
    End Sub

    Private Sub ConnectToDataBase()
        System.Threading.Thread.Sleep(1000) ' simulated "work"
    End Sub

    Private Sub LoadInterfaceObjects()
        System.Threading.Thread.Sleep(1000) ' simulated "work"
    End Sub

    Private Sub LoadClients()
        System.Threading.Thread.Sleep(1000) ' simulated "work"
    End Sub

End Class
公共类表单1
私有子表单1_Load(发送方作为对象,e作为事件参数)处理MyBase.Load
InitApplication()
端接头
私有子应用程序()
作为LoaderClass的Dim Loader=DirectCast(My.Application.SplashScreen,LoaderClass)
加载器显示百分比(1/10)
LoadLocalConfiguration()
加载器显示百分比(2/10)
ConnectToDataBase()
加载器显示百分比(3/10)
LoadInterfaceObjects()
加载器显示百分比(4/10)
LoadClients()

'Loader.CloseForm()你能解释一下吗?我应该用BeginInvoke替换Invoke吗?“我有一个主窗体,它需要执行一些长操作。”将工作放在不同的线程中,并让两个窗体都在主UI线程中运行!例外情况是,如果这个“工作”在应用程序加载时只完成一次,则将其保留在主UI线程上,并将LoaderClass()设置为初始表单。当启动屏幕在不同的线程中运行时,您仍然需要Invoke()在它们之间进行通信。谢谢您的建议。请问怎么做?我应该在哪里添加Invoke?我想你在做后一个选项?您是否已将LoaderClass()设置为Project-->属性中的“启动屏幕”?这几乎正是我在over at EE中的设置。