Task, progreso y cancelacion(dos)

Descripción general

Este documento muestra como enviar un informe de progresos, y como cancelar la tarea, ademas , la tarea se realiza en otra clase

[TOC] Tabla de Contenidos


↑↑↑

El formulario con la tarea

Es un formulario con dos botones [Prueba] y [Cancelar] y un TextBox donde se muestra el proceso [TextBox1]

el proceso esta en una clase independiente y consiste en la busqueda de los ficheros de un disco

se utiliza una clase propia [Util.Numeros.PorcentajeRealizadoEventArgs] para el evento de porcentaje

Observa el juego de las palabras claves [Async] y [Await] en el botón [Prueba]

    Public Class FormDos

    '------------
    ' El informe del progreso
    Private progress As New Progress(Of Util.Numeros.PorcentajeRealizadoEventArgs) _
                                   (Sub(percentaje)
                                        TextBox1.Text = percentaje.ProcentajeRealizado & "%"
                                    End Sub)

    '------------
    ' Define el Token de cancelacion.
    Private sourceCancellationToken As New System.Threading.CancellationTokenSource()
    Private tokenCancellation As System.Threading.CancellationToken = sourceCancellationToken.Token

    '------------
    ' La función que hace el trabajo esta en la clase [objClasePruebaDos]
    ' y en esta clase también se almacena el resultado del proceso
    Private objClasePruebaDos As New PruebaDosClase






    '------------
    Private Async Sub ButtonPrueba_Click(sender As Object, e As EventArgs) Handles ButtonPrueba.Click

  

        '----------------------------
        ' Apunte Táctico / problema
        ' quiero que cada vez que llamo a la función se ejecute
        ' si he llamado al cancelación, la función se detiene, y cuando vuelco a llamarla el [tokenCancellation]
        ' tiene el valor [esta cancelada], y , evidentemente, la función no vuelve a ejecutarse.
        ' para resolver este problema he estado haciendo muchas pruebas, y al final,
        ' he dado con esta solución, que no se si es la mejor, pero que funciona
        '
        ' fuera de la función se definen las variables [Source] y [Token]
        ' antes de empezar a trabajar, se usa el dispose para el [source]
        ' a continuación se vuelve a instanciar las dos variables  [Source] y [Token]
        ' y el proceso vuelve a funcionar cada vez que se llama.
        '-----------------------------------------------
        sourceCancellationToken.Dispose()
        sourceCancellationToken = New System.Threading.CancellationTokenSource()
        tokenCancellation = sourceCancellationToken.Token



      ' El directorio lo pongo aquí en el código porque es mas sencillo
        ' también se puede escoger (por ejemplo) de un TextBox del Form
        Dim directorioInicio As New System.IO.DirectoryInfo("C:\")

        ' el proceso Task, que devuelve un valor
        ' en est caso no lo uso para nada
        Dim r As Integer = Await Task.Run(Function()
                                              Return objClasePruebaDos.RecorrerDirRecursiva(directorioInicio, progress, tokenCancellation)
                                          End Function)


        TextBox1.Text = String.Format("Hecho!   [{0}], [{1}]", r, objClasePruebaDos.listaFicherosObservable.Count)

    End Sub

    '------------
    Private Sub ButtonCancelar_Click(sender As Object, e As EventArgs) Handles ButtonCancelar.Click
        sourceCancellationToken.Cancel()
    End Sub

End Class


↑↑↑

El formulario con la tarea (Actualizacion 2021/02/05)

otra forma de lanzar la tarea que tambien funciona

Mas información en Programación asincrónica basada en tareas

#Region "Proceso TASK - Botones Funciones con las acciones "
    '------------
    ' El informe del progreso
    Private ReadOnly progress As New Progress(Of Util.Numeros.PorcentajeRealizadoEventArgs) _
                                   (Sub(percentaje)
                                        Me.ProgressBarEjecutado.Value = percentaje.ProcentajeRealizado
                                        TextBoxInformes.Text = percentaje.Identificador
                                    End Sub)
    '------------
    ' Define el Token de cancelación.
    Private sourceCancellationToken As New System.Threading.CancellationTokenSource()
    Private tokenCancellation As System.Threading.CancellationToken = sourceCancellationToken.Token
    '------------
    ' La función que hace el trabajo esta en la clase [objClasePruebaDos]
    ' y en esta clase también se almacena el resultado del proceso
    ' es el objeto [CustomData]
    Private WithEvents ObjClaseBuscadorFicheros As LayerModelo.BusquedaFicherosIgualesTask
    ''' <summary>
    ''' Acción a realizar al pulsar el botón [Ejecutar]
    ''' </summary>
    Private Sub AccionButtonEjecutar()
        '----------------------------
        ' Apunte Táctico / problema
        ' quiero que cada vez que llamo a la función se ejecute
        ' si he llamado al cancelación, la función se detiene, y cuando vuelco a llamarla el [tokenCancellation]
        ' tiene el valor [esta cancelada], y , evidentemente, la función no vuelve a ejecutarse.
        ' para resolver este problema he estado haciendo muchas pruebas, y al final,
        ' he dado con esta solución, que no se si es la mejor, pero que funciona
        '
        ' fuera de la función se definen las variables [Source] y [Token]
        ' antes de empezar a trabajar, se usa el dispose para el [source]
        ' a continuación se vuelve a instanciar las dos variables  [Source] y [Token]
        ' y el proceso vuelve a funcionar cada vez que se llama.
        '-----------------------------------------------
        'sourceCancellationToken.Dispose()
        sourceCancellationToken = New System.Threading.CancellationTokenSource()
        tokenCancellation = sourceCancellationToken.Token
        ' el objeto que tiene las funciones para hacer el trabajo
        Dim directorioInicialBusqueda As String = "E:\"
        ObjClaseBuscadorFicheros = New LayerModelo.BusquedaFicherosIgualesTask(progress, tokenCancellation, directorioInicialBusqueda)
        ' Crear una tarea y proporcionar un delegado de usuario mediante una expresión lambda.
        Dim taskA As Task(Of Integer) = New Task(Of Integer)(Function()
                                                                 Call ObjClaseBuscadorFicheros.ExecuteTask()
                                                                 Return 1
                                                             End Function)
        ' arrancar la ejecución de la tarea.
        taskA.Start()
        ' esperar a que termine la tarea
        taskA.Wait()
        ' a) obtener el resultado
        Dim resultado As Integer = taskA.Result
        ' b) obtener  - Ejecutar tareas en función del valor de TaskStatus
        '               Valor que hay que actualizar en []
        Select Case ObjClaseBuscadorFicheros.TaskStatus
            Case Is = TaskStatus.RanToCompletion
                ' la tarea termino de ejecutarse correctamente
                Call ActualizarListViewDataBindig()
            Case Is = TaskStatus.Faulted
                ' la tarea termino debido a una excepción no controlada
                ' ESTE VALOR ES UN ERROR
                ' el proceso no debe continuar
                ' registra el error
                ' Ventana a su situación inicial
                TextBoxInformes.Text & = ObjClaseBuscadorFicheros.ResultadoProcesoTaskValidationResult.ErrorContent.ToString
                'objClasePruebaDos.ToNotepad()
            Case Is = TaskStatus.Canceled
                ' El usuario cancelo la operación
                ' Proceso cancelado por el usuario.
                ' Ventana a su situación inicial
                TextBoxInformes.Text & = ObjClaseBuscadorFicheros.ResultadoProcesoTaskValidationResult.ErrorContent.ToString
                TextBoxInformes.Text & = Environment.NewLine
        End Select
    End Sub
    '------------
    ''' <summary>
    ''' Acción a realizar al pulsar el botón [Cancelar]
    ''' </summary>
    Private Sub AccionButtonCancelar()
        Call sourceCancellationToken.Cancel()
    End Sub
    ''' <summary>
    ''' Acción a realizar al pulsar el botón [Terminar]
    ''' </summary>
    Private Sub AccionButtonTerminar()
        '------------------------------------------------------------
        ' Este es el proceso que se realiza al pulsar el botón [Terminar]
        Me.Close()
        '------------------------------------------------------------
    End Sub
#End Region

La clase que hace el trabajo

A continuación se muestra la clase que contiene el código que realizara la tarea (Task) del formulario

''' <summary>
'''  Clase que contiene la tarea que tiene que realizar el Task
''' </summary>
Public Class PruebaDosClase


    Public Property ContadorVueltas As Integer
    Public Property listaFicherosObservable As New System.Collections.ObjectModel.ObservableCollection(Of System.IO.FileInfo)
    Public ReadOnly Property IncluirBusquedaEnSubdirectorio As Boolean = True


    Public Sub New()
     ' No hacer nada (de momento)
    End Sub


    ''' <summary>
    '''  Recorre el directorio (y opcionalmente, según el valor de la propiedad 
    '''  [IncluirBusquedaEnSubdirectorio] los subdirectorios hijos) y 
    '''  guarda los ficheros el la propiedad de la clase [listaFicherosObservable]
    ''' </summary>
    ''' <param name=di">El directorio de inicio de la búsqueda</param>"
    ''' <param name=progress">el objeto [IProgress] para informar al llamador del progreso de la operación</param>"
    ''' <param name=tokenCancelacion">el token para cancelar el proceso</param>"
    ''' <returns>
    ''' Esta función es recursiva, 
    ''' devuelve  0 = falso si hay algún error,  y 1 = verdadero si acaba de forma normal 
    ''' </returns>
    Public Function RecorrerDirRecursiva(
                            ByVal di As System.IO.DirectoryInfo,
                            ByVal progress As IProgress(Of Util.Numeros.PorcentajeRealizadoEventArgs),
                            ByVal tokenCancelacion As System.Threading.CancellationToken) As Integer


        If progress IsNot Nothing Then
            ' como no se cuantos ficheros voy a encontrar, cada 5000 ficheros empiezo de nuevo a contar
            ' de esta forma el progress bar, se llena y luego vuelve a empezar  llenarse otra vez
            ContadorVueltas += 1
            If ContadorVueltas >= 5000 Then
                ContadorVueltas = 1
            End If
            progress.Report(New Util.Numeros.PorcentajeRealizadoEventArgs(ContadorVueltas, 5000, di.FullName))
        End If


        ' Recorrer los ficheros de este directorio
        Dim fics() As System.IO.FileInfo
        Dim dirs() As System.IO.DirectoryInfo

        Try
            If Not (tokenCancelacion = Nothing) Then
                If tokenCancelacion.IsCancellationRequested = True Then
                    Exit Try
                End If
            End If

            ' Primero leer los ficheros
            fics = di.GetFiles("*", System.IO.SearchOption.TopDirectoryOnly)
            For Each fi As System.IO.FileInfo In fics
                listaFicherosObservable.Add(fi)
            Next

            'Después leer los directorios
            If IncluirBusquedaEnSubdirectorio = True Then
                dirs = di.GetDirectories()
                For Each dir As System.IO.DirectoryInfo In dirs
                    RecorrerDirRecursiva(dir, progress, tokenCancelacion)
                Next
            End If

        Catch ex As Exception
            Console.WriteLine(ex.Message)
            Return 0

        End Try
        Return 1
    End Function

End Class

↑↑↑

A.2.Enlaces

[Para saber mas]
[Grupo de documentos]
[Documento Index]
[Documento Start]
[Imprimir el Documento]
© 1997 - - La Güeb de Joaquín
Joaquín Medina Serrano
Ésta página es española

Codificación
Fecha de creación [2016-09-13T08:41:17]
Última actualización
[HTML5 Desarrollado usando CSS3]