Cita de: kub0x en 14 Mayo 2016, 06:33 AMhttps://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx (con esto se arregla )
El compañero @KuBox ya te ha dado y explicado la solución, particionar la lógica del algoritmo en bloques de método individuales es irse por las ramas, ya que sigues sin evaluar la propiedad que se te mencionó.
Pero aparte del uso de la propiedad Control.Invoke, es muy recomendable que también utilices la propiedad Control.InvokeRequired para evitar intentar invocar el control de forma síncrona en caso de que haya sido creado desde el mismo hilo, ya que de lo contrario esto ocasionaría comportamientos indeseados y/o errores, como por ejemplo la recreación del handle del control;
También hay otros controles de errores adicionales que serían bastante imprescindibles como por ejemplo:
· Utilizar la declaración SyncLock para evitar que múltiples hilos intenten modificar el control, por ejemplo si instancias e inicias el mismo BackGroundWorker 2 veces al mismo tiempo.
· Comprobar si los recursos del control fueron liberados (Control.IsDisposed).
· Comprobar si el handle de la ventana se creó, y verificarlo antes de llamar a Control.InvokeRequired, ya que de lo contrario Control.InvokeRequired siempre devolverá False indiferentemente del hilo donde se creó el control.
...Pero por el momento podemos dejarlo con la medida de seguridad básica y principal, que sería, como ya he dicho, evaluar el valor devuelto por Control.InvokeRequired.
Este sería el código que puedes utilizar:
Código (vbnet) [Seleccionar]
Imports System.Threading
Public NotInheritable Class Work : Implements IDisposable
#Region " Private Fields "
<EditorBrowsable(EditorBrowsableState.Never)>
Friend WithEvents Bgw As BackgroundWorker
Private ReadOnly mreSync As ManualResetEvent
Private ReadOnly mreAsync As ManualResetEvent
Private isRunSync As Boolean = False
Private isCancelSyncRequested As Boolean = False
Private isPauseRequested As Boolean = False
#End Region
#Region " Properties "
<EditorBrowsable(EditorBrowsableState.Always)>
Public ReadOnly Property IsRunning As Boolean
Get
Return (Me.Bgw IsNot Nothing) AndAlso Not (Me.isPausedB)
End Get
End Property
<EditorBrowsable(EditorBrowsableState.Always)>
Public ReadOnly Property IsPaused As Boolean
Get
Return (Me.Bgw IsNot Nothing) AndAlso (Me.isPausedB)
End Get
End Property
Private isPausedB As Boolean = False
Public ReadOnly Property IsDisposed As Boolean
<DebuggerStepThrough>
Get
Return (Me.Bgw Is Nothing)
End Get
End Property
#End Region
#Region " Constructors "
<DebuggerNonUserCode>
Public Sub New()
Me.Bgw = New BackgroundWorker()
Me.mreSync = New ManualResetEvent(initialState:=False)
Me.mreAsync = New ManualResetEvent(initialState:=True)
End Sub
#End Region
#Region " Public Methods "
<DebuggerStepThrough>
Public Sub Run()
If Not (Me.IsDisposed) AndAlso Not (Me.Bgw.IsBusy) Then
Me.isRunSync = True
With Me.Bgw
.WorkerSupportsCancellation = False
.WorkerReportsProgress = False
.RunWorkerAsync()
End With
Me.mreSync.WaitOne()
Else
Throw New InvalidOperationException("BackGroundWorker is already running.")
End If
End Sub
<DebuggerStepThrough>
Public Sub RunAsync()
If Not (Me.IsDisposed) AndAlso Not (Me.Bgw.IsBusy) Then
With Me.Bgw
.WorkerSupportsCancellation = True
.WorkerReportsProgress = True
.RunWorkerAsync()
End With
Else
Throw New InvalidOperationException("BackGroundWorker is already running.")
End If
End Sub
<DebuggerStepThrough>
Public Sub Pause()
If Not (Me.IsDisposed) AndAlso Not (Me.isPausedB) Then
Me.isPauseRequested = True
Me.isPausedB = True
Me.mreAsync.Reset()
Else
Throw New InvalidOperationException("BackGroundWorker is not running.")
End If
End Sub
<DebuggerStepThrough>
Public Sub [Resume]()
If Not (Me.IsDisposed) AndAlso (Me.isPausedB) Then
Me.isPausedB = False
Me.isPauseRequested = False
Me.mreAsync.Set()
Else
Throw New InvalidOperationException("BackGroundWorker is not paused.")
End If
End Sub
<DebuggerStepThrough>
Public Sub Cancel(Optional ByVal throwOnError As Boolean = False)
Me.isCancelSyncRequested = True
Me.CancelAsync()
Me.mreSync.WaitOne()
Me.isCancelSyncRequested = False
End Sub
<DebuggerStepThrough>
Public Sub CancelAsync(Optional ByVal throwOnError As Boolean = False)
If Not (Me.IsDisposed) AndAlso Not (Me.Bgw.CancellationPending) AndAlso (Me.Bgw.IsBusy) Then
Me.mreAsync.Set() ' Resume thread if it is paused.
Me.Bgw.CancelAsync()
Else
Throw New InvalidOperationException("BackGroundWorker is not initialized.")
End If
End Sub
#End Region
#Region " Private Methods "
<DebuggerStepThrough>
Private Function AddLvItem(ByVal src As ListView, ByVal dst As ListView, ByVal index As Integer) As ListViewItem
Dim result As ListViewItem = Nothing
If (dst.InvokeRequired) Then
dst.Invoke(
Sub()
Try
result = dst.Items.Add(src.Items(index).Text)
Catch ex As Exception
Debug.WriteLine(String.Format("Woker exception occured: {0}", ex.Message))
End Try
End Sub)
Else
Try
result = dst.Items.Add(src.Items(index).Text)
Catch ex As Exception
Debug.WriteLine(String.Format("Woker exception occured: {0}", ex.Message))
End Try
End If
Return result
End Function
#End Region
#Region " Event-Handlers "
<DebuggerStepperBoundary>
Private Sub MyWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
Handles Bgw.DoWork
Dim lock As Object = ""
SyncLock lock
Dim lv1 As ListView = WindowsApplication1.Form1.ListView1
Dim lv2 As ListView = WindowsApplication1.Form1.ListView2
Dim max As Integer = lv2.Items.Count
Dim itemIndex As Integer = -1
For i As Integer = 0 To (max - 1)
If (Me.Bgw.CancellationPending) Then
e.Cancel = True
Exit For
Else
If Me.isPauseRequested Then ' Pause this thread.
Me.mreAsync.WaitOne(Timeout.Infinite)
End If
Dim lvi As ListViewItem = Me.AddLvItem(src:=lv2, dst:=lv1, index:=Interlocked.Increment(itemIndex))
If (lvi IsNot Nothing) Then
Debug.WriteLine(String.Format("ListViewItem Text: {0}", lvi.Text))
End If
If Me.Bgw.WorkerReportsProgress Then
Me.Bgw.ReportProgress((i + 1) * (100 \ max))
End If
Thread.Sleep(TimeSpan.FromSeconds(1))
End If
Next i
End SyncLock
If (Me.Bgw.WorkerReportsProgress) AndAlso Not (Me.Bgw.CancellationPending) Then
Me.Bgw.ReportProgress(100)
End If
If (Me.isRunSync) OrElse (Me.isCancelSyncRequested) Then
Me.mreSync.Set()
End If
End Sub
<DebuggerStepperBoundary>
Private Sub Bgw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
Handles Bgw.ProgressChanged
Debug.WriteLine(String.Format("Work Progress: {0:00.00}%", e.ProgressPercentage))
End Sub
<DebuggerStepperBoundary>
Private Sub Bgw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _
Handles Bgw.RunWorkerCompleted
If (e.Cancelled) Then
Debug.WriteLine("Work state: Cancelled")
ElseIf (e.Error IsNot Nothing) Then
Debug.WriteLine("Work state: Error")
Else
Debug.WriteLine("Work state: Success")
End If
Me.Dispose()
End Sub
#End Region
#Region " IDisposable Implementation "
Private isDisposedB As Boolean
<DebuggerStepThrough>
Public Sub Dispose() Implements IDisposable.Dispose
Me.Dispose(isDisposing:=True)
GC.SuppressFinalize(obj:=Me)
End Sub
<DebuggerStepThrough>
Private Sub Dispose(ByVal isDisposing As Boolean)
If (Not Me.isDisposedB) AndAlso (isDisposing) Then
Me.Bgw.Dispose()
Me.Bgw = Nothing
With Me.mreSync
.SafeWaitHandle.Close()
.SafeWaitHandle.Dispose()
.Close()
.Dispose()
End With
With Me.mreAsync
.SafeWaitHandle.Close()
.SafeWaitHandle.Dispose()
.Close()
.Dispose()
End With
End If
Me.isDisposedB = True
End Sub
#End Region
#Region " Hidden Methods "
<EditorBrowsable(EditorBrowsableState.Never)>
<DebuggerNonUserCode>
Public Shadows Function GetHashCode() As Integer
Return MyBase.GetHashCode
End Function
<EditorBrowsable(EditorBrowsableState.Never)>
<DebuggerNonUserCode>
Public Shadows Function [GetType]() As Type
Return MyBase.GetType
End Function
<EditorBrowsable(EditorBrowsableState.Never)>
<DebuggerNonUserCode>
Public Shadows Function Equals(ByVal obj As Object) As Boolean
Return MyBase.Equals(obj)
End Function
<EditorBrowsable(EditorBrowsableState.Never)>
<DebuggerNonUserCode>
Public Shadows Function ToString() As String
Return MyBase.ToString
End Function
#End Region
End Class
Ejemplo de uso:
Código (vbnet) [Seleccionar]
Public NotInheritable Class Form1 : Inherits Form
Friend MyWork As Work
Private Sub Button1_Click() Handles Button1.Click
If (Me.MyWork IsNot Nothing) Then
Me.MyWork.Cancel()
End If
Me.MyWork = New Work
Me.MyWork.RunAsync()
End Sub
End Class
Como se puede ver, la clase donde encapsulo el BackGroundWorker tiene varias funcionalidades añadidas para controlar operaciones sincrónicas y asincrónicas, si quieres ver la implementación completa y original, y con la documentación XML, aquí lo tienes:
Saludos.