elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Trabajando con las ramas de git (tercera parte)


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  Evitar se congele el formulario al hacer un for
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] 2 Ir Abajo Respuesta Imprimir
Autor Tema: Evitar se congele el formulario al hacer un for  (Leído 6,362 veces)
P4nd3m0n1um


Desconectado Desconectado

Mensajes: 1.420



Ver Perfil
Evitar se congele el formulario al hacer un for
« en: 14 Mayo 2016, 05:04 am »

Tengo un formulario con un listview y un boton, el botón realiza un proceso con cada item de la lista mediante un for, tengo otro listview que es un log, que cada vez que se ejecuta una linea se envía un aviso al log (se agrega un item a listview log), el problema esta en que este for congela al formulario mientras realiza el proceso, una vez que termina, muestra todo el log y se cumplieron todos los procesos pero necesitaría que el mismo no se congele, ya que el usuario tiene que estar pendiente del log para saber si esta funcionando correctamente o no.

Probe con colocar el log en otro formulario y antes de realizar el for abrir dicho formulario para que este quede activo, pero igual se congela tanto el primero como el segundo.

Alguna idea?


En línea

kub0x
Enlightenment Seeker
Moderador
***
Desconectado Desconectado

Mensajes: 1.461


S3C M4NI4C


Ver Perfil
Re: Evitar se congele el formulario al hacer un for
« Respuesta #1 en: 14 Mayo 2016, 05:12 am »

Deberías utilizar Threads (hilo) para separar la carga computacional y dejar el main thread (la GUI) sin carga para que se renderice de forma normal, ya que lo que está ocurriendo es que el for invalida o bloquea el proceso de renderizado de la GUI.

https://msdn.microsoft.com/en-us/library/system.threading.thread%28v=vs.110%29.aspx

Saludos!


En línea

Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate

P4nd3m0n1um


Desconectado Desconectado

Mensajes: 1.420



Ver Perfil
Re: Evitar se congele el formulario al hacer un for
« Respuesta #2 en: 14 Mayo 2016, 06:27 am »

Probe con el BackgroundWorker pero me da error al querer completar un listview que esta fuera del proceso:

Código
  1. Me.ListView1.Items.Add("Test LOG")

Error:

Código
  1. An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
  2. Additional information: Operación no válida a través de subprocesos: Se tuvo acceso al control 'ListView1' desde un subproceso distinto a aquel en que lo creó.
En línea

kub0x
Enlightenment Seeker
Moderador
***
Desconectado Desconectado

Mensajes: 1.461


S3C M4NI4C


Ver Perfil
Re: Evitar se congele el formulario al hacer un for
« Respuesta #3 en: 14 Mayo 2016, 06:33 am »

No quería decírtelo para no desalentarte en el proceso de gestión de hilos, pero esto es normal. Me explico, si tienes dos hilos cada uno tiene sus recursos locales y obviamente el hilo de la lógica del for no dispone de los recursos del hilo principal (refiriéndome a los controles ej: ListView) por lo tanto tienes que hacer una llamada desde el hilo del for al main thread para gestionar el control.

¿Cómo lo hacemos? Con delegados, que si programaste alguna vez en C/C++ no son más que pointers a function. Es decir, cuando termines el for en el segundo hilo llamas al function pointer que apunta a una func que reside en el primer hilo, de esta forma intercambias la información.

Ésta es la técnica general propuesta por Microsoft para gestionar dicha comunicación: https://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx (con esto se arregla ;) )

Saludos
En línea

Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate

P4nd3m0n1um


Desconectado Desconectado

Mensajes: 1.420



Ver Perfil
Re: Evitar se congele el formulario al hacer un for
« Respuesta #4 en: 14 Mayo 2016, 07:04 am »

Haber si entendi, usar el BackgroundWorker y cuando se haga el for en vez de mandarle el codigo, llamar a un función:

Código
  1.    Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles hiloSegundoPlano.DoWork
  2.        Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
  3.        Call  PasarList2aList1()
  4.    End Sub
  5.  
  6.    Function PasarList2aList1(ByVal n As Integer,ByVal worker As BackgroundWorker,ByVal e As DoWorkEventArgs) As Long
  7.        For i = 0 To ListView2.Items.Count - 1
  8.            Dim oreg As New ListViewItem
  9.            Threading.Thread.Sleep("2000")
  10.            oreg = Me.ListView1.Items.Add(ListView2.Items(i).Text)
  11.        Next
  12.        Return
  13.    End Function
En línea

kub0x
Enlightenment Seeker
Moderador
***
Desconectado Desconectado

Mensajes: 1.461


S3C M4NI4C


Ver Perfil
Re: Evitar se congele el formulario al hacer un for
« Respuesta #5 en: 14 Mayo 2016, 07:13 am »

Haber si entendi, usar el BackgroundWorker y cuando se haga el for en vez de mandarle el codigo, llamar a un función

No sé si lo habrás probado, pero no funcionará. Simplemente has separado la lógica del for en otra función (PasarList2aList1).

Lo que tienes que hacer es declarar un delegado que acepte un parámetro de tipo Integer, asignarle la función PasarList2aList1 al objeto delegado e invocarlo desde el BackgroundWorker1_DoWork haciendo Listview1.Invoke(delegado). De esta forma llamas al puntero de la función PasarList2aList1 desde DoWork de manera segura (como pide .NET, c# o vb trabajan igual). Revisa el link que postee antes.

Saludos!
En línea

Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate

fary
Colaborador
***
Desconectado Desconectado

Mensajes: 958



Ver Perfil WWW
Re: Evitar se congele el formulario al hacer un for
« Respuesta #6 en: 14 Mayo 2016, 09:24 am »

Prueba con DoEvents dentro del ciclo for. Así evitarás la sobrecarga, que es lo que hace que se te pete. :P

https://msdn.microsoft.com/es-es/library/system.windows.forms.application.doevents%28v=vs.110%29.aspx

PD: kub0x, al final terminaste escuchando a moraio chico  :laugh: ;-)

saludos!
En línea

Un byte a la izquierda.
kub0x
Enlightenment Seeker
Moderador
***
Desconectado Desconectado

Mensajes: 1.461


S3C M4NI4C


Ver Perfil
Re: Evitar se congele el formulario al hacer un for
« Respuesta #7 en: 14 Mayo 2016, 10:21 am »

Prueba con DoEvents dentro del ciclo for. Así evitarás la sobrecarga, que es lo que hace que se te pete. :P

Es una solución parcial y no debería de ser implementada en un ámbito profesional o incluso amateur, dado que se considerá mala práctica en el mundo de .NET. Te dirán que .NET no es vb6.

DoEvents permite procesar los eventos que están en la cola del Event Loop, cosa que tu for está ralentizando, de esta forma los mensajes de renderizado del formulario se procesarán y se dibujará sin problemas y sin hilos (todo en el main thread).

PD: kub0x, al final terminaste escuchando a moraio chico  :laugh: ;-)

P.D: Vaya bulerías que se montaba el morao, único en el compás, no he encontrado nada igual, supongo que será intrínseco de Jerez.

Saludos!
En línea

Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.700



Ver Perfil
Re: Evitar se congele el formulario al hacer un for
« Respuesta #8 en: 14 Mayo 2016, 15:08 pm »


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
  1. Imports System.Threading
  2.  
  3. Public NotInheritable Class Work : Implements IDisposable
  4.  
  5. #Region " Private Fields "
  6.  
  7.    <EditorBrowsable(EditorBrowsableState.Never)>
  8.    Friend WithEvents Bgw As BackgroundWorker
  9.  
  10.    Private ReadOnly mreSync As ManualResetEvent
  11.    Private ReadOnly mreAsync As ManualResetEvent
  12.  
  13.    Private isRunSync As Boolean = False
  14.    Private isCancelSyncRequested As Boolean = False
  15.    Private isPauseRequested As Boolean = False
  16.  
  17. #End Region
  18.  
  19. #Region " Properties "
  20.  
  21.    <EditorBrowsable(EditorBrowsableState.Always)>
  22.    Public ReadOnly Property IsRunning As Boolean
  23.        Get
  24.            Return (Me.Bgw IsNot Nothing) AndAlso Not (Me.isPausedB)
  25.        End Get
  26.    End Property
  27.  
  28.    <EditorBrowsable(EditorBrowsableState.Always)>
  29.    Public ReadOnly Property IsPaused As Boolean
  30.        Get
  31.            Return (Me.Bgw IsNot Nothing) AndAlso (Me.isPausedB)
  32.        End Get
  33.    End Property
  34.    Private isPausedB As Boolean = False
  35.  
  36.    Public ReadOnly Property IsDisposed As Boolean
  37.        <DebuggerStepThrough>
  38.        Get
  39.            Return (Me.Bgw Is Nothing)
  40.        End Get
  41.    End Property
  42.  
  43. #End Region
  44.  
  45. #Region " Constructors "
  46.  
  47.    <DebuggerNonUserCode>
  48.    Public Sub New()
  49.  
  50.        Me.Bgw = New BackgroundWorker()
  51.        Me.mreSync = New ManualResetEvent(initialState:=False)
  52.        Me.mreAsync = New ManualResetEvent(initialState:=True)
  53.  
  54.    End Sub
  55.  
  56. #End Region
  57.  
  58. #Region " Public Methods "
  59.  
  60.    <DebuggerStepThrough>
  61.    Public Sub Run()
  62.  
  63.        If Not (Me.IsDisposed) AndAlso Not (Me.Bgw.IsBusy) Then
  64.            Me.isRunSync = True
  65.            With Me.Bgw
  66.                .WorkerSupportsCancellation = False
  67.                .WorkerReportsProgress = False
  68.                .RunWorkerAsync()
  69.            End With
  70.            Me.mreSync.WaitOne()
  71.  
  72.        Else
  73.            Throw New InvalidOperationException("BackGroundWorker is already running.")
  74.  
  75.        End If
  76.  
  77.    End Sub
  78.  
  79.    <DebuggerStepThrough>
  80.    Public Sub RunAsync()
  81.  
  82.        If Not (Me.IsDisposed) AndAlso Not (Me.Bgw.IsBusy) Then
  83.            With Me.Bgw
  84.                .WorkerSupportsCancellation = True
  85.                .WorkerReportsProgress = True
  86.                .RunWorkerAsync()
  87.            End With
  88.  
  89.        Else
  90.            Throw New InvalidOperationException("BackGroundWorker is already running.")
  91.  
  92.        End If
  93.  
  94.    End Sub
  95.  
  96.    <DebuggerStepThrough>
  97.    Public Sub Pause()
  98.  
  99.        If Not (Me.IsDisposed) AndAlso Not (Me.isPausedB) Then
  100.            Me.isPauseRequested = True
  101.            Me.isPausedB = True
  102.            Me.mreAsync.Reset()
  103.  
  104.        Else
  105.            Throw New InvalidOperationException("BackGroundWorker is not running.")
  106.  
  107.        End If
  108.  
  109.    End Sub
  110.  
  111.    <DebuggerStepThrough>
  112.    Public Sub [Resume]()
  113.  
  114.        If Not (Me.IsDisposed) AndAlso (Me.isPausedB) Then
  115.            Me.isPausedB = False
  116.            Me.isPauseRequested = False
  117.            Me.mreAsync.Set()
  118.  
  119.        Else
  120.            Throw New InvalidOperationException("BackGroundWorker is not paused.")
  121.  
  122.        End If
  123.  
  124.    End Sub
  125.  
  126.    <DebuggerStepThrough>
  127.    Public Sub Cancel(Optional ByVal throwOnError As Boolean = False)
  128.  
  129.        Me.isCancelSyncRequested = True
  130.        Me.CancelAsync()
  131.        Me.mreSync.WaitOne()
  132.        Me.isCancelSyncRequested = False
  133.  
  134.    End Sub
  135.  
  136.    <DebuggerStepThrough>
  137.    Public Sub CancelAsync(Optional ByVal throwOnError As Boolean = False)
  138.  
  139.        If Not (Me.IsDisposed) AndAlso Not (Me.Bgw.CancellationPending) AndAlso (Me.Bgw.IsBusy) Then
  140.            Me.mreAsync.Set() ' Resume thread if it is paused.
  141.            Me.Bgw.CancelAsync()
  142.  
  143.        Else
  144.            Throw New InvalidOperationException("BackGroundWorker is not initialized.")
  145.  
  146.        End If
  147.  
  148.    End Sub
  149.  
  150. #End Region
  151.  
  152. #Region " Private Methods "
  153.  
  154.    <DebuggerStepThrough>
  155.    Private Function AddLvItem(ByVal src As ListView, ByVal dst As ListView, ByVal index As Integer) As ListViewItem
  156.  
  157.        Dim result As ListViewItem = Nothing
  158.  
  159.        If (dst.InvokeRequired) Then
  160.            dst.Invoke(
  161.                Sub()
  162.                    Try
  163.                        result = dst.Items.Add(src.Items(index).Text)
  164.                    Catch ex As Exception
  165.                        Debug.WriteLine(String.Format("Woker exception occured: {0}", ex.Message))
  166.                    End Try
  167.                End Sub)
  168.        Else
  169.            Try
  170.                result = dst.Items.Add(src.Items(index).Text)
  171.            Catch ex As Exception
  172.                Debug.WriteLine(String.Format("Woker exception occured: {0}", ex.Message))
  173.            End Try
  174.  
  175.        End If
  176.  
  177.        Return result
  178.  
  179.    End Function
  180.  
  181. #End Region
  182.  
  183. #Region " Event-Handlers "
  184.  
  185.    <DebuggerStepperBoundary>
  186.    Private Sub MyWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
  187.    Handles Bgw.DoWork
  188.  
  189.        Dim lock As Object = ""
  190.        SyncLock lock
  191.  
  192.            Dim lv1 As ListView = WindowsApplication1.Form1.ListView1
  193.            Dim lv2 As ListView = WindowsApplication1.Form1.ListView2
  194.            Dim max As Integer = lv2.Items.Count
  195.            Dim itemIndex As Integer = -1
  196.  
  197.            For i As Integer = 0 To (max - 1)
  198.  
  199.                If (Me.Bgw.CancellationPending) Then
  200.                    e.Cancel = True
  201.                    Exit For
  202.  
  203.                Else
  204.                    If Me.isPauseRequested Then ' Pause this thread.
  205.                        Me.mreAsync.WaitOne(Timeout.Infinite)
  206.                    End If
  207.  
  208.                    Dim lvi As ListViewItem = Me.AddLvItem(src:=lv2, dst:=lv1, index:=Interlocked.Increment(itemIndex))
  209.                    If (lvi IsNot Nothing) Then
  210.                        Debug.WriteLine(String.Format("ListViewItem Text: {0}", lvi.Text))
  211.                    End If
  212.  
  213.                    If Me.Bgw.WorkerReportsProgress Then
  214.                        Me.Bgw.ReportProgress((i + 1) * (100 \ max))
  215.                    End If
  216.  
  217.                    Thread.Sleep(TimeSpan.FromSeconds(1))
  218.  
  219.                End If
  220.  
  221.            Next i
  222.  
  223.        End SyncLock
  224.  
  225.        If (Me.Bgw.WorkerReportsProgress) AndAlso Not (Me.Bgw.CancellationPending) Then
  226.            Me.Bgw.ReportProgress(100)
  227.        End If
  228.  
  229.        If (Me.isRunSync) OrElse (Me.isCancelSyncRequested) Then
  230.            Me.mreSync.Set()
  231.        End If
  232.  
  233.    End Sub
  234.  
  235.    <DebuggerStepperBoundary>
  236.    Private Sub Bgw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
  237.    Handles Bgw.ProgressChanged
  238.  
  239.        Debug.WriteLine(String.Format("Work Progress: {0:00.00}%", e.ProgressPercentage))
  240.  
  241.    End Sub
  242.  
  243.    <DebuggerStepperBoundary>
  244.    Private Sub Bgw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _
  245.    Handles Bgw.RunWorkerCompleted
  246.  
  247.        If (e.Cancelled) Then
  248.            Debug.WriteLine("Work state: Cancelled")
  249.  
  250.        ElseIf (e.Error IsNot Nothing) Then
  251.            Debug.WriteLine("Work state: Error")
  252.  
  253.        Else
  254.            Debug.WriteLine("Work state: Success")
  255.  
  256.        End If
  257.  
  258.        Me.Dispose()
  259.  
  260.    End Sub
  261.  
  262. #End Region
  263.  
  264. #Region " IDisposable Implementation "
  265.  
  266.    Private isDisposedB As Boolean
  267.  
  268.    <DebuggerStepThrough>
  269.    Public Sub Dispose() Implements IDisposable.Dispose
  270.  
  271.        Me.Dispose(isDisposing:=True)
  272.        GC.SuppressFinalize(obj:=Me)
  273.  
  274.    End Sub
  275.  
  276.    <DebuggerStepThrough>
  277.    Private Sub Dispose(ByVal isDisposing As Boolean)
  278.  
  279.        If (Not Me.isDisposedB) AndAlso (isDisposing) Then
  280.            Me.Bgw.Dispose()
  281.            Me.Bgw = Nothing
  282.  
  283.            With Me.mreSync
  284.                .SafeWaitHandle.Close()
  285.                .SafeWaitHandle.Dispose()
  286.                .Close()
  287.                .Dispose()
  288.            End With
  289.  
  290.            With Me.mreAsync
  291.                .SafeWaitHandle.Close()
  292.                .SafeWaitHandle.Dispose()
  293.                .Close()
  294.                .Dispose()
  295.            End With
  296.  
  297.        End If
  298.  
  299.        Me.isDisposedB = True
  300.  
  301.    End Sub
  302.  
  303. #End Region
  304.  
  305. #Region " Hidden Methods "
  306.  
  307.    <EditorBrowsable(EditorBrowsableState.Never)>
  308.    <DebuggerNonUserCode>
  309.    Public Shadows Function GetHashCode() As Integer
  310.        Return MyBase.GetHashCode
  311.    End Function
  312.  
  313.    <EditorBrowsable(EditorBrowsableState.Never)>
  314.    <DebuggerNonUserCode>
  315.    Public Shadows Function [GetType]() As Type
  316.        Return MyBase.GetType
  317.    End Function
  318.  
  319.    <EditorBrowsable(EditorBrowsableState.Never)>
  320.    <DebuggerNonUserCode>
  321.    Public Shadows Function Equals(ByVal obj As Object) As Boolean
  322.        Return MyBase.Equals(obj)
  323.    End Function
  324.  
  325.    <EditorBrowsable(EditorBrowsableState.Never)>
  326.    <DebuggerNonUserCode>
  327.    Public Shadows Function ToString() As String
  328.        Return MyBase.ToString
  329.    End Function
  330.  
  331. #End Region
  332.  
  333. End Class

Ejemplo de uso:
Código
  1. Public NotInheritable Class Form1 : Inherits Form
  2.  
  3.    Friend MyWork As Work
  4.  
  5.    Private Sub Button1_Click() Handles Button1.Click
  6.  
  7.        If (Me.MyWork IsNot Nothing) Then
  8.            Me.MyWork.Cancel()
  9.        End If
  10.  
  11.        Me.MyWork = New Work
  12.        Me.MyWork.RunAsync()
  13.  
  14.    End Sub
  15.  
  16. 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.
« Última modificación: 14 Mayo 2016, 20:18 pm por Eleкtro » En línea


P4nd3m0n1um


Desconectado Desconectado

Mensajes: 1.420



Ver Perfil
Re: Evitar se congele el formulario al hacer un for
« Respuesta #9 en: 14 Mayo 2016, 22:00 pm »

probe pero me da:

Código:
An exception of type 'System.InvalidOperationException' occurred in AutoLog.exe but was not handled in user code

Additional information: Error al crear el formulario. Consulte Exception.InnerException para obtener más detalles. Error: No se puede crear una instancia del control ActiveX '8856f961-340a-11d0-a96b-00c04fd705a2' porque el subproceso actual no está en un contenedor uniproceso.

En delphi no pasaba esto..  :-( jajaja
« Última modificación: 14 Mayo 2016, 22:09 pm por P4nd3m0n1um » En línea

Páginas: [1] 2 Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
como puedo hacer un formulario
Diseño Gráfico
cotin 2 1,326 Último mensaje 23 Marzo 2005, 14:38 pm
por cotin
Como hacer formulario php?
PHP
illo05 9 2,440 Último mensaje 19 Mayo 2010, 23:36 pm
por illo05
Como evitar el minimizado de un formulario??? « 1 2 »
.NET (C#, VB.NET, ASP)
TomaSs 18 8,759 Último mensaje 22 Octubre 2012, 16:39 pm
por TomaSs
AYUDA, VALIDAR DATOS Y EVITAR INYECCION EN FORMULARIO A BASE DE DATOS
Desarrollo Web
antonioska 4 4,619 Último mensaje 1 Abril 2013, 00:39 am
por antonioska
Evitar cierre de formulario vb6
Programación General
Buggcon 2 3,212 Último mensaje 8 Abril 2013, 04:18 am
por Eleкtro
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines