检查后出现错误"Cannot access a disposed object"。IsDisposed() in VB.NET



为了跟踪VB.NET应用程序中长时间运行的异步任务的进度,我创建了一个包含ProgressBar控件的小窗体。该任务通过Invoke更新ProgressBar,我检查以确保控件不是Nothing,并且它不是IsDisposed。这个想法是,如果用户不想看着ProgressBar表单慢慢完成,他们可以关闭它。

然而,在测试中,我关闭了表单,然后得到了一个错误";System.ObjectDisposedException:"无法访问已释放的对象。"我的猜测是表单在更新时关闭了。也就是说,在我检查它是否正常之后,但在它完成之前。

Private Sub Btn_StoreForceStale_Click(sender As Button, e As EventArgs) Handles Btn_StoreForceStale.Click
Dim pb As ProgressBar = GetProgressPopUp("Force Refreshing All Stale Items in Inventory")
EnableButtons(False)
Dim x As Task = Task.Run(Sub() RefreshPrices(True,, pb)).ContinueWith(Sub() EnableButtons(True))
End Sub
Public Sub RefreshPrices(Optional force As Boolean = False, Optional PriceAge As Integer = -1, Optional progBar As TextProgressBar = Nothing)
'Actual price refreshing Code
If Not (progBar Is Nothing OrElse progBar.IsDisposed) Then
progBar.Invoke(Sub() progBar.Increment(1))  '<-- Error happens here
End If
End Sub

崩溃后,我正在调试中,我可以看到progBar.IsDisposed为true,因此不应该发生增量。这就是为什么我认为表单关闭发生在增量过程中。

有办法避免这种情况吗?Form.Clossing事件中的一种方法,用于检查控件是否被调用,并将关闭延迟到未被调用?还是发生了其他事情?

谢谢!

有办法避免这种情况吗?

是。有很多方法。一种选择是使用Progress(Of T)来处理更新。在启动任务之前,在UI线程中创建类的实例。它将捕获当前的同步上下文并使用它,而不是试图通过特定的UI对象进行调用。

另一种方法是使用变量来指示是否应该报告进度。您可以处理FormClosed事件来设置变量。如果这样做,则需要使用某种同步(例如SyncLock(来确保进度更新仅在变量为False时发生,并且UI线程在调用BeginInvoke()之后才能将其设置为True。请注意,在这种方法中,必须使用BeginInvoke(),因为在进行调用时需要持有锁,如果在这种情况下使用Invoke(),则调用将死锁。

正如您所猜测的,我更喜欢Progress(Of T)选项。:(对我来说,它似乎没有那么乱了。

最后,您的问题中没有足够的上下文来了解是否可以使用AsyncAwait。但是,如果您可以将长期运行的任务重新组合为一个循环,在每次迭代中使用Task.Run(),等待它,然后更新进度条,那么IMHO将是理想的方法。注意,循环的每个迭代不需要对应于单个工作单元;您可以(如果单个工作单元很小,也应该(批量完成工作,这样就不会过于频繁地在工作线程和UI线程之间进行转换。

最新更新