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中的设置。